2013-04-22 4 views
2

У меня есть таблица с сотнями тысяч строк, назначенная пользователю. например:MySQL: удалите из таблицы все, кроме 20 строк?

itemid | userid | etc 
1  | 1  | etc 
2  | 1  | etc 
3  | 1  | etc 
4  | 3  | etc 
5  | 3  | etc 
6  | 3  | etc 
etc | etc | etc 

Пользователь может иметь любое количество предметов, назначенных ему или ей. То есть Любое число от 0 до бесконечности. Моя проблема заключается в том, что я хочу, чтобы SQL-запрос удалял все элементы для каждого пользователя, но сохранил 20. Если у пользователя меньше 20, например, только 10 назначенных предметов, он должен хранить все 10.

Как я могу сделай это?

ОБНОВЛЕНИЕ

Если пользователь имеет 50 пунктов, с идентификаторами 1 - 50, он должен возвратить детали 30 - 50. Другими словами, последние 20 вставленные элементы для этого пользователя.

+1

Wont простая 'DELETE * FROM table WHERE itemid> 20' work? – Daanvn

+3

Есть ли вид товаров? Вы хотите сохранить 20 случайных предметов или 20 последних вставленных предметов? – Marcassin

+0

@Daanvn Я думал то же самое, но, я думаю, это не будет - см. Itemid выглядит как столбец AI – MythThrazz

ответ

-1

Это работает для меня:

DELETE FROM table WHERE itemid NOT IN ( 
     SELECT itemid 
     FROM ( 
      SELECT * 
      FROM table 
      WHERE userid = 1 
      ORDER BY itemid DESC 
      LIMIT 20 
     ) x 
    ) 
    AND userid = 1 
+2

Я думаю, что OP хочет сделать это для ВСЕХ пользователей. – Strawberry

2

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

CREATE TABLE my_table(itemid INT NOT NULL AUTO_INCREMENT PRIMARY KEY,userid INT NOT NULL); 

INSERT INTO my_table VALUES 
(1, 1), 
(2, 1), 
(3, 1), 
(4, 3), 
(5, 3), 
(6, 3); 

SELECT * FROM my_table; 
+--------+--------+ 
| itemid | userid | 
+--------+--------+ 
|  1 |  1 | 
|  2 |  1 | 
|  3 |  1 | 
|  4 |  3 | 
|  5 |  3 | 
|  6 |  3 | 
+--------+--------+ 

Вот запрос, чтобы выбрать строки для удаления ...

SELECT a.* 
    FROM my_table a 
    LEFT 
    JOIN 
    (SELECT x.*, COUNT(*) FROM my_table x JOIN my_table y ON y.userid = x.userid AND y.itemid >= x.itemid GROUP BY x.itemid HAVING COUNT(*) <=2)b 
    ON b.itemid = a.itemid 
WHERE b.itemid IS NULL; 
+--------+--------+ 
| itemid | userid | 
+--------+--------+ 
|  1 |  1 | 
|  4 |  3 | 
+--------+--------+ 

. ..И вот запрос, чтобы удалить их ...

DELETE a 
    FROM my_table a 
    LEFT 
    JOIN 
    (SELECT x.*, COUNT(*) FROM my_table x JOIN my_table y ON y.userid = x.userid AND y.itemid >= x.itemid GROUP BY x.itemid HAVING COUNT(*) <=2)b 
    ON b.itemid = a.itemid 
WHERE b.itemid IS NULL; 

SELECT * FROM my_table; 
+--------+--------+ 
| itemid | userid | 
+--------+--------+ 
|  2 |  1 | 
|  3 |  1 | 
|  5 |  3 | 
|  6 |  3 | 
+--------+--------+ 
+0

Хороший ответ. Для получения дополнительной информации этот URL помог мне понять этот материал http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/, когда решение аналогичной проблемы –

0

попробовать это

 delete from Fruits where itemid 
    not in (select itemid from (select itemid from Fruits where userid = 1 
    ORDER BY itemid desc limit 20)x ) 
    and userid=1 

DEMO HERE

+0

это удалит все записи и опустится всего лишь на 20 для userid = 1 –

+0

@PavelDubinin вы были правы, я отредактировал свой ответ, спасибо за уловку :) –

+0

Я считаю, что он хочет сделать это для всех пользователей сразу, поэтому с таким подходом запрос, который вы предлагаете, должен быть выполнен для каждого пользователя отдельно (что может привести к тысячам запросов). Это очень неэффективно –

0

Этот код работает следующим образом:

  • Создает новый столбец: Count - который подсчитывает элементы для каждого пользователя
  • Заказы результаты по убыванию элемента - так, чтобы первые события для пользователя получили наивысшие идентификаторы
  • Выбор/удаляет все строки с графом> 20 - оставив последние 20 (или менее) событий интактных

SELECT:

SET @count=1; 
SET @last_user_id=0; 

SELECT * FROM (
SELECT *, 
case 
when (@last_user_id = userid) 
then 
@count:[email protected]+1 
else @count:=1 end as count, 
case 
when (@last_user_id!=userid) 
then 
@last_user_id:=userid 
end 
as new 
FROM <tablename> ORDER BY userid, itemid DESC) AS inner_table WHERE count > 20 

DELETE:

SET @count=1; 
SET @last_user_id=0; 

DELETE FROM (
SELECT *, 
CASE 
WHEN (@last_user_id = userid) 
THEN 
@count:[email protected]+1 
ELSE 
@count:=1 
END 
AS count, 
CASE 
WHEN (@last_user_id!=userid) 
THEN 
@last_user_id:=userid 
END 
AS new 
FROM <tablename> ORDER BY userid, itemid DESC) AS inner_table WHERE count > 20 
Смежные вопросы