Топ N на группу с объединением нескольких таблиц - PullRequest
7 голосов
/ 16 марта 2011

Судя по моим исследованиям, это очень распространенная проблема, которая обычно имеет довольно простое решение.Моя задача - изменить несколько запросов из получить все результаты в получить топ-3 в группе .Сначала все шло хорошо, и я использовал для этого несколько рекомендаций и ответов с этого сайта (Самые популярные продукты).Тем не менее, я столкнулся с трудностями с моим последним "бестселлером" из-за нескольких объединений.

По сути, мне нужно привести все продукты в порядок на # самых высоких продаж на продукт, при котором максимальное количество продуктов на одного поставщика составляет 3 У меня есть несколько таблиц, соединенных для создания исходного запросаи каждый раз, когда я пытаюсь использовать переменные для генерации рейтинга, это приводит к неверным результатам.Следующее должно помочь лучше понять проблему (я удалил ненужные поля для краткости):

Таблица продуктов

productid | vendorid | approved | active | deleted

Таблица поставщиков

vendorid | approved | active | deleted

Таблица заказов

orderid | `status` | deleted

Таблица элементов заказа

orderitemid | orderid | productid | price

Теперь мой исходный запрос к получить все результаты выглядит следующим образом:

SELECT COUNT(oi.price) AS `NumSales`, 
       p.productid, 
       p.vendorid
FROM products p
INNER JOIN vendors v ON (p.vendorid = v.vendorid)
INNER JOIN orders_items oi ON (p.productid = oi.productid)
INNER JOIN orders o ON (oi.orderid = o.orderid)
WHERE (p.Approved = 1 AND p.Active = 1 AND p.Deleted = 0)
AND (v.Approved = 1 AND v.Active = 1 AND v.Deleted = 0)
AND o.`Status` = 'SETTLED'
AND o.Deleted = 0
GROUP BY oi.productid
ORDER BY COUNT(oi.price) DESC
LIMIT 100;

Наконец,(и вот где я в тупике), я пытаюсь изменить вышеприведенное утверждение так, чтобы я получал только 3 лучших продукта (по # проданным) на одного поставщика.Я бы добавил то, что у меня есть, но мне стыдно, и этот вопрос уже является стеной текста.Я пробовал переменные, но продолжаю получать неверные результаты.Любая помощь будет принята с благодарностью.

Ответы [ 3 ]

10 голосов
/ 16 марта 2011

Несмотря на то, что вы указали LIMIT 100, для этого типа запроса потребуется полное сканирование и создание таблицы, после чего каждая проверенная запись и строка нумеруются перед окончательной фильтрацией для 100, которое вы хотите отобразить.

select
    vendorid, productid, NumSales
from
(
    select
        vendorid, productid, NumSales,
        @r := IF(@g=vendorid,@r+1,1) RowNum,
        @g := vendorid
    from (select @g:=null) initvars
    CROSS JOIN 
    (
        SELECT COUNT(oi.price) AS NumSales, 
               p.productid, 
               p.vendorid
        FROM products p
        INNER JOIN vendors v ON (p.vendorid = v.vendorid)
        INNER JOIN orders_items oi ON (p.productid = oi.productid)
        INNER JOIN orders o ON (oi.orderid = o.orderid)
        WHERE (p.Approved = 1 AND p.Active = 1 AND p.Deleted = 0)
        AND (v.Approved = 1 AND v.Active = 1 AND v.Deleted = 0)
        AND o.`Status` = 'SETTLED'
        AND o.Deleted = 0
        GROUP BY p.vendorid, p.productid
        ORDER BY p.vendorid, NumSales DESC
    ) T
) U
WHERE RowNum <= 3
ORDER BY NumSales DESC
LIMIT 100;

Подход здесь

  1. Сгруппировать, чтобы получить NumSales
  2. Использовать переменные для нумерации строк продаж по поставщику / продукту
  3. Отфильтруйте нумерованный набор данных, чтобы максимально 3 на каждого поставщика
  4. Закажите оставшиеся у NumSales DESC и верните только 100
0 голосов
/ 09 февраля 2016

Ответ, данный @RichardTheKiwi, работал отлично и помог мне пройти 99% пути! Я использую MySQL и получаю только первую строку каждой группы, помеченную номером строки, в то время как остальные строки остаются пустыми. Это привело к тому, что запрос возвратил только верхнее попадание для каждой группы, а не первые три строки. Чтобы это исправить, я должен был инициализировать @r в подзапросе initvars. Я изменился,

from (select @g:=null) initvars

до

from (select @g:=null, @r:=null) initvars

Вы также можете инициализировать @r в 0, и это будет работать так же. А для тех, кто менее знаком с этим типом синтаксиса, дополнительный раздел читает каждую отсортированную группу и, если строка имеет тот же vendorid, что и предыдущая строка, которая отслеживается переменной @g, она увеличивает номер строки , который хранится в переменной @r. Когда этот процесс достигает следующей группы с новым vendorid, оператор IF больше не будет оцениваться как true, а переменная @r (и, следовательно, RowNum) будет сброшена в 1.

0 голосов
/ 26 июля 2012

Мне нравится это элегантное решение, однако, когда я запускаю адаптированный, но похожий запрос на своем компьютере разработчика, я получаю недетерминированный набор результатов.Я полагаю, что это связано с тем, что оптимизатор MySql имеет дело с назначением и чтением пользовательских переменных внутри одного и того же оператора.общее правило, вы никогда не должны присваивать значение пользовательской переменной и читать значение в том же выражении.Вы можете получить ожидаемые результаты, но это не гарантировано.Порядок вычисления для выражений с участием пользовательских переменных не определен и может изменяться в зависимости от элементов, содержащихся в данном выражении;кроме того, этот порядок не гарантируется одинаковым между выпусками MySQL Server.

Просто добавьте эту заметку здесь на случай, если кто-то еще столкнется с этим странным поведением.

...