Как найти три величайших значения в каждой категории в PostgreSQL? - PullRequest
0 голосов
/ 18 июня 2019

Я новичок в SQL. У меня проблемы с тем, как найти 3 максимальных значения в каждой категории. Вопрос был

"Для идентификаторов заказов в январе 2006 года, какие были самые высокие (по доходам) 3 идентификатора продукта для каждого идентификатора категории?"

Table A:                    
(Column name)         
customer_id            
order_id              
order_date   
revenue  
product_id

Table B:  
product_id  
category_id

Я попытался объединить таблицу B и A, используя Inner Join, и отфильтровал по order_date. Но затем я застрял на том, как найти 3 максимальных значения в каждом category_id. Спасибо.

Это то, что я могу думать о

SELECT B.product_id, category_id FROM A

JOIN B ON B.product_id = A.product_id

WHERE order_date BETWEEN ‘2006-01-01’ AND ‘2006-01-31’

ORDER BY revenue DESC

LIMIT 3;

Ответы [ 4 ]

2 голосов
/ 18 июня 2019

Этот тип запроса обычно решается с помощью оконных функций

select *
from (
  SELECT b.product_id, 
         b.category_id,
         a.revenue,
         dense_rank() over (partition by b.category_id, b.product_id order by a.revenue desc) as rnk
  from A
    join b ON B.product_id = A.product_id
  where a.order_date between date '2006-01-01' AND date '2006-01-31'
) as t
where rnk <= 3
order by product_id, category_id, revenue desc;

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

Если один и тот же продукт может появляться более одного раза в таблице b (для одной и той же категории), вам необходимо объединить его с GROUP BY, чтобы получить сумму всех доходов:

select *
from (
  SELECT b.product_id, 
         b.category_id,
         sum(a.revenue) as total_revenue,
         dense_rank() over (partition by b.category_id, a.product_id order by sum(a.revenue) desc) as rnk
  from a
    join b on B.product_id = A.product_id
  where a.order_date between date '2006-01-01' AND date '2006-01-31'
  group by b.product_id, b.category_id
) as t
where rnk <= 3
order by product_id, category_id, total_revenue desc;

При объединении оконных функций и GROUP BY оконная функция будет применяться после GROUP BY.

1 голос
/ 18 июня 2019

Вы можете использовать оконные функции для сбора сгруппированного дохода, а затем получить последний X во внешнем запросе.Я немного не работал в PostgreSQL, поэтому мне может не хватать функции быстрого доступа ниже.

WITH ByRevenue AS
(
    --This creates a virtualized table that can be queried similar to a physical table in the conjoined statements below 
    SELECT
        category_id,
        product_id,
        MAX(revenue) as max_revenue 
    FROM 
        A
        JOIN B ON B.product_id = A.product_id
    WHERE 
        order_date BETWEEN ‘2018-01-01’ AND ‘2018-01-31’
    GROUP BY
        category_id,product_id
)
,Normalized
(
    --Pull data from the in memory table above using normal sql syntax and normalize it with a RANK function to achieve the limit.
    SELECT
        category_id,
        product_id,
        max_revenue,
        ROW_NUMBER() OVER (PARTITION BY category_id,product_id ORDER BY max_revenue DESC) as rn
    FROM
        ByRevenue
)
--Final query from stuff above with each category/product ranked by revenue
SELECT * 
FROM Normalized 
WHERE RN<=3;
0 голосов
/ 18 июня 2019

Для топ-n запросов первое, что нужно попробовать, обычно это боковое соединение:

WITH categories as (
    SELECT DISTINCT category_id
    FROM B
)
SELECT categories.category_id, sub.product_id
FROM categories
JOIN LATERAL (
    SELECT a.product_id
    FROM B
    JOIN A ON (a.product_id = b.product_id)
    WHERE b.category_id = categories.category_id
      AND order_date BETWEEN '2006-01-01' AND '2006-01-31'
    GROUP BY a.product_id
    ORDER BY sum(revenue) desc
    LIMIT 3
) sub on true;
0 голосов
/ 18 июня 2019

Попробуйте использовать только выборку n строк?

Примечание. Давайте подумаем, что ваш первичный ключ здесь product_id, поэтому я использовал их для объединения двух таблиц.

SELECT A.category,A.revenue From Table A 
INNER JOIN Table B on A.product_id = B.Product_ID 
WHERE A.Order_Date between (from date) and (to date)
ORDER BY A.Revenue DESC
Fetch first 3 rows only
...