Плотное ранжирование Postgresql должно начинаться с 2, если начальная разница равна 1

#postgresql #postgresql-9.3

#postgresql #postgresql-9.3

Вопрос:

Итак, у меня есть таблица и запрос, которые ранжируют стоимость товаров и не допускают привязки к позиции 1, если есть привязка к позиции 1, ранжирование начинается со 2.

Вот схема с образцом данных

   CREATE TABLE applications
      (id int, name char(10), cost int);

  INSERT INTO applications
      (id, name, cost)
  VALUES
      (1, 'nfhfjs', 10),
      (2, 'oopdld', 20),
      (3, 'Wedass', 14),
      (4, 'djskck', 22),
      (5, 'laookd', 25),
      (6, 'mfjjf', 25),
      (7, 'vfhgg', 28),
      (8, 'nvopq', 29),
      (9, 'nfhfj', 56),
      (10, 'voapp', 56);
  

Вот запрос

   WITH start_tie AS (
SELECT 
  DENSE_RANK() OVER(ORDER BY cost DESC) cost_rank,
  lead(cost,1) OVER (ORDER BY cost DESC) as next_app_cost
  FROM 
  applications LIMIT 1
)
  SELECT 
  *, 
  DENSE_RANK() OVER(ORDER BY cost DESC) cost_rank,
  (CASE start_tie.cost_rank WHEN start_tie.next_app_cost THEN cost_rank 1 ELSE cost_rank END) AS right_cost_rank
  FROM 
  applications;
  

мой ожидаемый результат

  id  name    cost  cost_rank
 10  voapp    56     2
 9   nfhfj    56     2
 8   nvopq    29     3
 7   vfhgg    28     4
 6   mfjjf    25     5
 5   laookd   25     5
 4   djskck   22     6
 2   oopdld   20     7
 3   Wedass   14     8
 1   nfhfjs   10     9
  

Пожалуйста, измените запрос для достижения результата.

SQL FIDDLE

Комментарии:

1. Что, если у вас есть три значения в качестве наивысшей стоимости?

Ответ №1:

Все, что вам нужно сделать, это проверить, совпадает ли самая высокая стоимость со второй по величине стоимостью. И если это так, добавьте 1 ко всем значениям ранга:

 with start_tie as (
  select case 
           when cost = lead(cost) over (order by cost desc) then 1 
           else 0
         end as tie_offset
  from applications
  order by cost desc
  limit 1
)
select *, 
       dense_rank() over (order by cost desc)   (select tie_offset from start_tie) cost_rank
from applications;
  

Пример: http://rextester.com/EKSLJK65530


Если количество связей определяет смещение, которое будет использоваться для «нового» ранжирования, смещение может быть рассчитано с использованием этого:

 with start_tie as (
  select count(*) - 1 as tie_offset
  from applications a1
  where cost = (select max(cost) from applications)
)
select *, 
       dense_rank() over(order by cost desc)   (select tie_offset from start_tie) cost_rank
from applications;
  

Комментарии:

1. что, если (10,10,10,20, …) не должно быть ранжирования, начинающегося с 3?

2. @cske: согласно вопросу » если есть разница в позиции 1, ранжирование начинается со 2 » — но вы правы, это немного двусмысленно

3. @cske это используется в сервисе торгов, у нас нет ничьей на первой позиции, ничья на остальных позициях — это нормально

Ответ №2:

Сначала нет связи, означает, что больше одной с рангом 1

замените r.cost_rank x.c-1 на r.cost_rank 1 , если фиксировано, ранжирование начинается с 2, независимо от того, сколько рангов в рангах связи

 WITH r AS (
    SELECT
       *
      ,DENSE_RANK() OVER(ORDER BY cost DESC) cost_rank
    FROM
      applications
), x as (select count(*) as c from r where cost_rank=1)
SELECT
  r.*, (CASE WHEN 1<x.c THEN r.cost_rank x.c-1 ELSE r.cost_rank END) as fixed
FROM
  r,x;