Допустим, у меня есть этот фиктивный стол

CREATE TABLE dim_product (
    product_id int4 null primary key,
    product_name varchar(100) NULL
);
CREATE TABLE dim_territory (
    territory_id int4 null primary key,
    region varchar(30) NULL
);    
CREATE TABLE fact_sales (
    order_detail_id varchar(30) null primary key,
    product_id int4 NULL,
    territory_id int4 NULL,
    quantity int4 NULL,
    foreign key(product_id) references dim_product(product_id),
    foreign key(territory_id) references dim_territory(territory_id)
);

insert into dim_product 
(product_id, product_name)
values
(1, 'patch kit'), 
(2, 'mountain bike'), 
(3, 'logo');        
insert into dim_territory 
(territory_id, region)
values
(1, 'AUS'),
(2, 'FRN'),
(3, 'GRMN');    
insert into fact_sales
(order_detail_id, product_id, territory_id, quantity)
values
('z1', 1, 1, 1),
('z2', 1, 1, 3),
('z3', 2, 1, 4),
('z4', 3, 2, 4),
('z5', 3, 2, 2),
('y1', 2, 1, 1),
('y2', 1, 3, 4),
('y3', 2, 3, 5),
('x4', 3, 3, 1),
('x5', 3, 2, 4);

Я хочу знать 2 лучших продукта в каждом регионе в зависимости от количества

Я пробовал с этим кодом, но меня это смутило lol: D

select DT.region, DP.product_name, sum(FS.quantity) as quantity
from fact_sales FS 
     join dim_territory DT
     on FS.territory_id=DT.territory_id
     join dim_product DP
     on FS.territory_id=DT.territory_id
group by product_name, region
order by quantity desc

Результат этого кода такой, это так неправильно

Регион product_name количество
Комплект патчей ГРМН 10
Логотип FRN 10
Логотип GRMN 10
Горный велосипед FRN 10
Велосипед горный ГРМН 10
Комплект патчей FRN 10
Горный велосипед AUS 9
Комплект патчей AUS 9
Логотип AUS 9

Как мне исправить это

1
Napier 17 Сен 2021 в 17:56

2 ответа

Лучший ответ

Взгляните на оконные функции, особенно на rank(), dense_rank() и row_number(), например

SELECT *, RANK() OVER w AS rank
FROM (
  SELECT DT.region, DP.product_name, sum(FS.quantity) AS quantity
  FROM fact_sales FS 
  JOIN dim_territory DT ON FS.territory_id=DT.territory_id
  JOIN dim_product DP ON FS.product_id=DP.product_id
  GROUP BY product_name, region
) j 
WINDOW w AS (PARTITION BY region ORDER BY quantity DESC
             RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW);

Если вы хотите отфильтровать строки с более низким рейтингом, поместите его в CTE (или другой подзапрос) и ограничьте результаты желаемым рангом:

WITH j AS (
  SELECT *, RANK() OVER w AS rank
  FROM (
    SELECT DT.region, DP.product_name, sum(FS.quantity) AS quantity
    FROM fact_sales FS 
    JOIN dim_territory DT ON FS.territory_id=DT.territory_id
    JOIN dim_product DP ON FS.product_id=DP.product_id
    GROUP BY product_name, region
  ) i 
  WINDOW w AS (PARTITION BY region ORDER BY quantity DESC
               RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
)
SELECT * FROM j WHERE rank <= 2;

Демо: db<>fiddle

1
Jim Jones 17 Сен 2021 в 15:40

У вас есть ошибка при втором соединении с используемыми вами переменными.

Попробуй это:

    select DT.region, DP.product_name, sum(FS.quantity) as quantity
from fact_sales FS 
     join dim_territory DT
     on FS.territory_id=DT.territory_id
     join dim_product DP
     on FS.product_id=DP.product_id
group by product_name, region
order by quantity desc
1
William Andrés Bernal 17 Сен 2021 в 15:05