Избавление от дублирующих результатов в MySQL запросе при использовании UNION - PullRequest
2 голосов
/ 01 октября 2009

У меня есть запрос MySQL, чтобы получить элементы, которые были недавно активны. По сути, пользователи могут оставлять отзывы или добавлять их в свой список желаний, и я хочу получить все элементы, которые либо имели новый отзыв за последние x дней, либо были добавлены в чей-то список желаний.

Запрос выглядит примерно так (немного упрощенно):

SELECT items.*, reaction.timestamp AS date FROM items
LEFT JOIN reactions ON reactions.item_id = items.id
WHERE reactions.timestamp > 1251806994
GROUP BY items.id

UNION

SELECT items.*, wishlists.timestamp AS date FROM items
LEFT JOIN wishlist ON wishlists.item_id = items.id
WHERE wishlists.timestamp > 1251806994
GROUP BY items.id

ORDER BY date DESC LIMIT 5

Это работает, но когда элемент был помещен как в чей-то список желаний , так и отзыв был опубликован, элемент возвращается дважды. UNION обычно удаляет дубликаты, но поскольку date отличается между двумя строками, возвращаются обе строки. Могу ли я как-то сказать MySQL игнорировать дату при удалении повторяющихся строк?

Я также пытался сделать что-то вроде этого:

SELECT items.*, IF(wishlists.id IS NOT NULL, wishlists.timestamp, reactions.timestamp) AS date FROM items
LEFT JOIN reactions ON reactions.item_id = items.id
LEFT JOIN wishlist ON wishlists.item_id = items.id

WHERE (wishlists.id IS NOT NULL AND wishlists.timestamp > 1251806994) OR
(reactions.id IS NOT NULL AND reactions.timestamp > 1251806994)
GROUP BY items.id

ORDER BY date DESC LIMIT 5

Но это почему-то оказалось безумно медленным (заняло около полминуты).

Ответы [ 3 ]

5 голосов
/ 01 октября 2009

Я решил это сам, основываясь на идее larryb82. Я в основном сделал следующее:

SELECT * FROM (
    SELECT items.*, reaction.timestamp AS date FROM items
    LEFT JOIN reactions ON reactions.item_id = items.id
    WHERE reactions.timestamp > 1251806994
    GROUP BY items.id

    UNION

    SELECT items.*, wishlists.timestamp AS date FROM items
    LEFT JOIN wishlist ON wishlists.item_id = items.id
    WHERE wishlists.timestamp > 1251806994
    GROUP BY items.id

    ORDER BY date DESC LIMIT 5
) AS items

GROUP BY items.id
ORDER BY date DESC LIMIT 5

Хотя я понимаю, что это, вероятно, не учитывает, какая дата является самой высокой для каждого элемента ... Еще не уверен, имеет ли это значение, и если да, что с этим делать.

1 голос
/ 02 октября 2009

Я не думаю, что вам нужен СОЮЗ здесь вообще.


SELECT item.*, GREATEST(COALESCE(wishlists.timestamp, 0), COALESCE(reaction.timestamp, 0)) as date
FROM items
LEFT JOIN reactions ON reactions.item_id = items.id AND reactions.timestamp > 1251806994
LEFT JOIN wishlists ON wishlists.item_id = items.id AND wishlists.timestamp > 1251806994
ORDER BY date DESC limit 5

Вы использовали LEFT JOIN выше, вероятно, очень медленно из-за предиката с ИЛИ в нем. Вы попросили базу данных объединить три таблицы, а затем проверили этот результат на наличие информации о метках времени. Мое утверждение должно сформировать меньшую промежуточную таблицу. Элементы, у которых нет ни реакции, ни списка желаний, получат дату 0, что, вероятно, приведет к тому, что о них не будет сообщено.

1 голос
/ 01 октября 2009

Не уверен, что это будет огромный удар по производительности, но вы можете попробовать

SELECT item_field_1, item_field_2, ..., max(date) as date
FROM
  (the query you posted) 
GROUP BY item_field_1, item_field_2, ...
...