97

最新のN(id descでソート)を除くすべてのレコードをテーブルから削除する単一のmysqlクエリ(変数なし)を構築することは可能ですか?

このようなもの、それだけは機能しません:)

delete from table order by id ASC limit ((select count(*) from table ) - N)

ありがとう。

4

16 に答える 16

151

そのようにレコードを削除することはできません。主な問題は、サブクエリを使用して 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 つのエラーが発生します。

  1. SQL エラー (1093): FROM 句で更新するターゲット テーブル 'table' を指定できません- MySQL では、直接サブクエリ内から削除するテーブルを参照できません。
  2. SQL エラー (1235): このバージョンの MySQL は、'LIMIT & IN/ALL/ANY/SOME サブクエリ' をまだサポートしていません- NOT IN 演算子の直接サブクエリ内で LIMIT 句を使用することはできません。

幸いなことに、中間サブクエリを使用すると、これらの両方の制限を回避できます。


Nicole は、このクエリは特定のユース ケース (このようなもの) に対して大幅に最適化できると指摘しています。その回答も読んで、自分に合っているかどうかを確認することをお勧めします。

于 2009-02-23T19:13:34.907 に答える
117

かなり古い質問を復活させていることは知っていますが、最近この問題に遭遇しましたが、大きな数にうまくスケーリングできるものが必要でした。既存のパフォーマンス データはありませんでした。この質問はかなりの注目を集めていたので、見つけたものを投稿しようと思いました。

実際に機能したソリューションは、Alex Barrett の double sub-query/メソッド ( NOT INBill 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
  );

を使用してNOFFSET番目のレコードの ID を取得し、そのレコードとそれ以前のすべてのレコードを削除します。

順序付けは既にこの問題の前提であるため ( ORDER BY id DESC)、<=完全に適合します。

サブクエリによって生成される一時テーブルには、 N 個のレコードではなく 1 つのレコードしか含まれないため、はるかに高速です。

テストケース

2 つのテスト ケースで、3 つの作業方法と上記の新しい方法をテストしました。

どちらのテスト ケースも既存の 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 |
+-----------+------------------------+----------------------+

興味深いのは、この<=方法は全体的にパフォーマンスが向上していることですが、実際には維持するほどパフォーマンスが向上し、悪化することはありません。

于 2011-11-28T22:41:29.353 に答える
11

残念ながら、他の人々からのすべての回答に対して、同じクエリで特定のテーブルから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'

私が思いつく最善の答えは、これを 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 回のクエリで作業を完了するわけではありませんが、より単純ですぐに実行できるソリューションが最も効果的な場合があります。

于 2009-02-23T19:08:00.193 に答える
9
DELETE  i1.*
FROM    items i1
LEFT JOIN
        (
        SELECT  id
        FROM    items ii
        ORDER BY
                id DESC
        LIMIT 20
        ) i2
ON      i1.id = i2.id
WHERE   i2.id IS NULL
于 2009-02-23T19:03:08.113 に答える
8

IDが増分の場合は、次のようなものを使用します

delete from table where id < (select max(id) from table)-N
于 2009-02-23T19:03:39.970 に答える
5

最後の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;

このアプローチの良い点はパフォーマンスです。約 13,000 レコードのローカル DB でクエリをテストし、最後の 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 年後半の i7 MacBookPro 上の mySQL 5.5.25

于 2013-10-02T08:12:08.353 に答える
2
DELETE FROM table WHERE ID NOT IN
(SELECT MAX(ID) ID FROM table)
于 2009-02-23T19:02:46.867 に答える
1

以下のクエリを試してください:

DELETE FROM tablename WHERE id < (SELECT * FROM (SELECT (MAX(id)-10) FROM tablename ) AS a)

内側のサブクエリは上位 10 件の値を返し、外側のクエリは上位 10 件を除くすべてのレコードを削除します。

于 2015-05-14T06:44:00.793 に答える
0

どうですか:

SELECT * FROM table del 
         LEFT JOIN table keep
         ON del.id < keep.id
         GROUP BY del.* HAVING count(*) > N;

以前にN行を超える行を返します。役に立つでしょうか?

于 2011-08-03T11:28:35.107 に答える
0

これもうまくいくはずです:

DELETE FROM [table] 
INNER JOIN (
    SELECT [id] 
    FROM (
        SELECT [id] 
        FROM [table] 
        ORDER BY [id] DESC
        LIMIT N
    ) AS Temp
) AS Temp2 ON [table].[id] = [Temp2].[id]
于 2009-02-23T20:18:47.200 に答える
0
DELETE FROM table WHERE id NOT IN (
    SELECT id FROM table ORDER BY id, desc LIMIT 0, 10
)
于 2009-02-23T19:05:02.290 に答える
0

多くの場合、このタスクに 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    
)
于 2012-02-14T10:28:53.373 に答える
-1

なぜだめですか

DELETE FROM table ORDER BY id DESC LIMIT 1, 123456789

2 番目の LIMIT 引数として非常に大きな数を使用して、最初の行以外をすべて削除します (順序は DESC です!)。こちらをご覧ください

于 2009-02-23T19:40:46.480 に答える
-1

久しぶりにこれに答える...同じ状況に遭遇し、言及された答えを使用する代わりに、以下を使用しました-

DELETE FROM table_name order by ID limit 10

これにより、最初の 10 件のレコードが削除され、最新のレコードが保持されます。

于 2013-02-19T05:45:42.783 に答える