2009-04-02 2 views
2

Предполагая, что у меня есть таблица foo где у меня есть что-то вроде этого:Как удалить все строки, которые не являются самыми новыми N для каждого пользователя в MySQL?

id, user_id, timestamp, some_value

То, что я хочу сделать, это удалить все строки, которые не являются новейшим N для каждого пользователя ,

Само удаление может быть обработан: с

DELETE FROM foo WHERE id NOT IN (...) 

, чтобы вы могли перефразировать проблему в: Как получить новейший N (там может быть меньше) строк для каждого пользователя. Это означает, что если у меня есть U-пользователи, я могу закончить с N * U-строками, поэтому LIMIT не будет работать.

ответ

2

MySQL не поддерживает чтение из таблицы с SELECT и выполняет UPDATE/INSERT/DELETE в той же таблице в том же запросе. Поэтому делать то, что вы хотите в одном из заявлений, будет сложно.

Я хотел бы сделать это в два этапа: первый, запросить новые $N записи для каждого пользователя, и хранить их во временной таблице:

CREATE TEMPORARY TABLE foo_top_n 
    SELECT f1.id 
    FROM foo f1 LEFT OUTER JOIN foo f2 
    ON (f1.user_id = f2.user_id AND f1.id < f2.id) 
    GROUP BY f1.id 
    HAVING COUNT(*) < $N; 

Далее используйте синтаксис с несколькими столами DELETE и присоединиться foo к временная таблица, не УДАЛЕНИЕ где не найдено совпадение:

DELETE f1 FROM foo f1 LEFT OUTER JOIN foo_top_n f2 USING (id) 
WHERE f2.id IS NULL; 
0

Во-первых, получить общее количество строк с помощью этого:

SELECT COUNT(*) as total FROM foo WHERE id NOT IN (...) 

Тогда попробуйте это:

DELETE FROM foo WHERE id NOT IN (...) ORDER BY timestamp ASC LIMIT (Count - N) 

заменой N на ваш номер. Это приведет к удалению всех, кроме новейших N строк. Например, , если есть всего 100 строк, и вы хотите сохранить новые 5, это приведет к удалению (100-5) самых старых 95 строк.

+0

Прошу прощения за то, что я не понял, я имел в виду N строк для каждого user_id – tliff

+0

В этом случае вы должны изменить запрос на: Where id = user_id. Я предполагаю, что у вас есть список всех идентификаторов в массиве, вы зацикливаете на каждый идентификатор и выполняете этот запрос для них –

0
DELETE FROM foo WHERE id NOT IN (SELECT id FROM foo ORDER BY timestamp DESC LIMIT N) 

Edit:

я неправильно понял вопрос. вы хотите сохранить N записей для каждого пользователя. Может быть, это:

SELECT user_id FROM foo 

Тогда для каждого user_id (как currentID):

DELETE FROM foo WHERE user_id=currentID AND id NOT IN (SELECT id FROM foo WHERE user_id=currentID ORDER BY timestamp DESC LIMIT N) 

(я не очень уверен в синтаксисе, но я надеюсь, что идея ясна)

+0

Мне жаль, что я не понял, я имел в виду N строк для каждого user_id – tliff

3

на самом деле, можно сделать это один запрос:

DELETE l.* 
FROM foo l 
JOIN (
     SELECT user_id, 
       COALESCE(
       (
       SELECT timestamp 
       FROM foo li 
       WHERE li.user_id = dlo.user_id 
       ORDER BY 
         li.user_id DESC, li.timestamp DESC 
       LIMIT 2, 1 
       ), CAST('0001-01-01' AS DATETIME)) AS mts, 
       COALESCE(
       (
       SELECT id 
       FROM foo li 
       WHERE li.user_id = dlo.user_id 
       ORDER BY 
         li.user_id DESC, li.timestamp DESC, li.id DESC 
       LIMIT 2, 1 
       ), -1) AS mid 
     FROM (
       SELECT DISTINCT user_id 
       FROM foo dl 
       ) dlo 
     ) lo 
ON  l.user_id = lo.user_id 
     AND (l.timestamp, l.id) < (mts, mid) 

Смотрите подробные объяснения здесь:

+0

+1 awesome query – Bigbohne