Получить 10 лучших продуктов для каждой категории - PullRequest
4 голосов
/ 11 февраля 2011

У меня есть запрос, похожий на этот

SELECT 
t.category, 
tc.product, 
tc.sub-product,
 count(*) as sales 
 FROM tg t, ttc tc
 WHERE t.value = tc.value
 GROUP BY t.category, tc.product, tc.sub-product;

Теперь в своем запросе я хочу получить топ-10 товаров для каждой категории (топ по продажам) и для каждой категории мне нужны топ-5 подкатегорий (топ по продажам)

Вы можете принять постановку задачи примерно так:

Получите 10 лучших продуктов для каждой категории по продажам, а для каждого продукта - 5 лучших субпродуктов по продажам.

  • Здесь категория может быть Книги
  • продуктом может быть книга Гарри Портера
  • субпродукт может быть HarryPorter серии 5

Пример формата входных данных

category |product |subproduct |Sales [count (*)]

abc   test1    test11     120

abc   test1    test11     100

abc   test1    test11     10

abc   test1    test11     10

abc   test1    test11     10

abc   test1    test11     10

abc   test1    test12     10

abc   test1    test13     8

abc   test1    test14     6

abc   test1    test15     5

abc   test2    test21     80

abc   test2    test22     60

abc   test3    test31     50

abc   test3    test32     40

abc   test4    test41     30

abc   test4    test42     20

abc   test5    test51     10

abc   test5    test52     5 

abc   test6    test61     5 

|

|

|

bcd   test2    test22     10 

xyz   test3    test31     5 

xyz   test3    test32     3 

xyz   test4    test41     2

Вывод будет "

top 5 rf for (abc) -> abc,test1(289) abc,test2 (140), abc test3 (90), abc test4(50) , abc test5 (15)

top 5 rfm for (abc,test1) -> test11(260),test12(10),test13(8),test14(6),test15(5) and so on

Мой запрос не выполняется, потому что результаты действительно огромные. Я читаю об аналитических функциях оракула, таких как ранг. Может кто-нибудь помочь мне изменить этот запрос с помощью аналитических функций. Любой другой подход также может работать.

Я имею в виду это http://www.orafaq.com/node/55. Но не могу получить правильный SQL-запрос для этого.

Буду признателен за любую помощь .. Я как бы застрял на 2 дня на этом: (

Ответы [ 2 ]

1 голос
/ 11 февраля 2011

Возможно, есть причины не использовать аналитические функции, а использовать аналитические функции в одиночку :

select am, rf, rfm, rownum_rf2, rownum_rfm
from
(
    -- the 3nd level takes the subproduct ranks, and for each equally ranked
    -- subproduct, it produces the product ranking
    select am, rf, rfm, rownum_rfm,
      row_number() over (partition by rownum_rfm order by rownum_rf) rownum_rf2
    from
    (
        -- the 2nd level ranks (without ties) the products within
        -- categories, and subproducts within products simultaneosly
        select am, rf, rfm,
          row_number() over (partition by am order by count_rf desc) rownum_rf,
          row_number() over (partition by am, rf order by count_rfm desc) rownum_rfm
        from
        (
            -- inner most query counts the records by subproduct
            -- using regular group-by. at the same time, it uses
            -- the analytical sum() over to get the counts by product
            select tg.am, ttc.rf, ttc.rfm,
              count(*) count_rfm,
              sum(count(*)) over (partition by tg.am, ttc.rf) count_rf
            from tg inner join ttc on tg.value = ttc.value
            group by tg.am, ttc.rf, ttc.rfm
        ) X
    ) Y
    -- at level 3, we drop all but the top 5 subproducts per product
    where rownum_rfm <= 5   -- top  5 subproducts
) Z
-- the filter on the final query retains only the top 10 products
where rownum_rf2 <= 10  -- top 10 products
order by am, rownum_rf2, rownum_rfm;

Я использовал rownum вместо ранга, чтобы вы никогда не получали связи, или, другими словами, связи будут выбираться случайным образом. Это также не работает, если данные недостаточно плотные (менее 5 субпродуктов в любом из 10 лучших продуктов - вместо них могут отображаться субпродукты из некоторых других продуктов). Но если данные плотные (большая установленная база данных), запрос должен работать нормально.


Ниже приведены два прохода данных, но в каждом случае возвращаются правильные результаты. Опять же, это запрос ранга без связей.
select am, rf, rfm, count_rf, count_rfm, rownum_rf, rownum_rfm
from
(
    -- next join the top 10 products to the data again to get
    -- the subproduct counts
    select tg.am, tg.rf, ttc.rfm, tg.count_rf, tg.rownum_rf, count(*) count_rfm,
        ROW_NUMBER() over (partition by tg.am, tg.rf order by 1 desc) rownum_rfm
    from (
        -- first rank all the products
        select tg.am, tg.value, ttc.rf, count(*) count_rf,
            ROW_NUMBER() over (order by 1 desc) rownum_rf
        from tg
        inner join ttc on tg.value = ttc.value
        group by tg.am, tg.value, ttc.rf
        order by count_rf desc
        ) tg
    inner join ttc on tg.value = ttc.value and tg.rf = ttc.rf
    -- filter the inner query for the top 10 products only
    where rownum_rf <= 10
    group by tg.am, tg.rf, ttc.rfm, tg.count_rf, tg.rownum_rf
) X
-- filter where the subproduct rank is in top 5
where rownum_rfm <= 5
order by am, rownum_rf, rownum_rfm;

столбцы:

count_rf : count of sales by product
count_rfm : count of sales by subproduct
rownum_rf : product rank within category (rownumber - without ties)
rownum_rfm : subproduct rank within product (without ties)
0 голосов
/ 11 февраля 2011

Это предположение, но вы, вероятно, можете начать с чего-то вроде этого:

drop table category_sales;

Некоторые тестовые данные:

create table category_sales (
   category    varchar2(14),
   product     varchar2(14),
   subproduct  varchar2(14),
   sales       number
);

begin

  for cate in 1 .. 10 loop
  for prod in 1 .. 20 loop
  for subp in 1 .. 30 loop

      insert into category_sales values (
             'Cat '  || cate,
             'Prod ' || cate||prod,
             'Subp ' || cate||prod||subp,
              trunc(dbms_random.value(1,30 + cate - prod + subp))
      );

  end loop; end loop; end loop;

end;
/

Фактический запрос:

select * from (
  select 
    category,
    product,
    subproduct,
    sales,
    category_sales,
    product_sales,
    top_subproduct,
    -- Finding best products within category:
    dense_rank () over (
      partition by category
      order     by product_sales desc
    ) top_product 
  from (
    select 
      -- Finding the best Subproducts within
      -- category and product:
      dense_rank () over (
         partition by category, 
                      product 
         order     by sales desc
      )                             top_subproduct,
      -- Finding the sum(sales) within a 
      -- category and prodcut
      sum(sales) over (
         partition by category, 
                      product
      )                             product_sales,
      -- Finding the sum(sales) within 
      -- category
      sum(sales) over (
         partition by category
      )                             category_sales,
      category,
      product,
      subproduct,
      sales
    from
      category_sales
  )
)
where 
--    Only best 10 Products
      top_product       <= 10 and
--    Only best 5 subproducts:
      top_subproduct    <= 5
-- "Best" categories first:
order by 
      category_sales desc,
      top_product    desc,
      top_subproduct desc;

В этом запросе столбец category_sales возвращает сумму продаж категории, в записи которой она возвращается.Это означает, что каждая запись той же категории имеет один и тот же category_sales.Этот столбец необходим, чтобы сначала упорядочить набор результатов с лучшими (продажными) категориями (order by ... category_sales desc).

Аналогично, product_sales - это сумма продаж для продукта-категории.сочетание.Этот столбец используется для поиска лучших n (здесь: 10) товаров в каждой категории (where top_product <= 10).

Столбец top_product «создан» с помощью аналитической функции dense_rank() over....Для лучшего продукта в категории - 1, для второго - 2 и т. Д. (Следовательно, where top_product <= 10.

Столбец top_suproduct рассчитывается аналогично top_product (чтос dense_rank).

...