Как я могу удалить все строки, которые не являются самыми новыми N для каждого пользователя в MySQL? - PullRequest
2 голосов
/ 03 апреля 2009

Если у меня есть таблица foo, где у меня есть что-то вроде этого:

id, user_id, timestamp, some_value

Что я хочу сделать, это удалить все строки, которые не являются самыми новыми N для пользователя .

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

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

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

Ответы [ 4 ]

3 голосов
/ 24 апреля 2009

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

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)

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

2 голосов
/ 03 апреля 2009

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 голосов
/ 03 апреля 2009
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 голосов
/ 03 апреля 2009

Сначала получите общее количество строк, используя это:

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 строк.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...