У меня есть сценарий, в котором я должен получить отрицательный рейтинг для значений ниже порогового предела в SQL. Может ли кто-нибудь помочь мне в этом?
Name Target Rank
John 2500 -2
Hopkins 4000 -1
Paul 5000 0
Gracia 5200 1
Выше приведен пример целевой таблицы. Мне нужно присвоить ранги, как показано. Для людей, имеющих значение 5000, следует присвоить «0». Для ресурса с рейтингом ниже 5000 рейтинг должен иметь отрицательное значение (-1, -2, -3...). Для ресурса выше 5000 рейтинг должен иметь положительное значение ранга (1,2,3). – введите описание изображения здесь


Используйте комбинацию аналитических функций ROW_NUMBER() и COUNT():
Настройка Oracle:
CREATE TABLE table_name ( Name, Target ) As
SELECT 'John', 2500 FROM DUAL UNION ALL
SELECT 'Hopkins', 4000 FROM DUAL UNION ALL
SELECT 'Paul', 5000 FROM DUAL UNION ALL
SELECT 'Gracia', 5200 FROM DUAL;
Запрос:
SELECT t.*,
ROW_NUMBER() OVER ( ORDER BY target ASC )
- COUNT( CASE WHEN target < 5000 THEN 1 END ) OVER ()
- 1 AS rnk
FROM table_name t;
Вывод:
NAME | TARGET | RNK :------ | -----: | --: John | 2500 | -2 Hopkins | 4000 | -1 Paul | 5000 | 0 Gracia | 5200 | 1
дб <> рабочий пример здесь
Обновлять:
Настройка Oracle:
CREATE TABLE table_name ( Name, Target ) As
SELECT 'John', 2500 FROM DUAL UNION ALL
SELECT 'Hopkins', 4000 FROM DUAL UNION ALL
SELECT 'Bob', 5000 FROM DUAL UNION ALL
SELECT 'Smith', 5000 FROM DUAL UNION ALL
SELECT 'Paul', 5100 FROM DUAL UNION ALL
SELECT 'Janet', 5100 FROM DUAL UNION ALL
SELECT 'Gracia', 5200 FROM DUAL;
Запрос 1: если вам нужен уникальный ранг для каждой строки и не нужна строка с рангом 0, если нет значения 5000:
SELECT t.*,
ROW_NUMBER() OVER ( ORDER BY target ASC, name ASC )
- COUNT( CASE WHEN target < 5000 THEN 1 END ) OVER ()
- CASE WHEN target > 5000 AND COUNT( CASE WHEN target = 5000 THEN 1 END ) OVER () = 0 THEN 0 ELSE 1 END AS rnk
FROM table_name t;
Вывод:
NAME | TARGET | RNK :------ | -----: | --: John | 2500 | -2 Hopkins | 4000 | -1 Bob | 5000 | 0 Smith | 5000 | 1 Janet | 5100 | 2 Paul | 5100 | 3 Gracia | 5200 | 4
Запрос 2: если вы хотите, чтобы строки с одной и той же целью имели одинаковый ранг и чтобы только цели из 5000 имели ранг 0:
SELECT name,
target,
DENSE_RANK() OVER ( ORDER BY target ASC )
- COUNT( CASE WHEN target < 5000 AND rn = 1 THEN 1 END ) OVER ()
- CASE WHEN target > 5000 AND COUNT( CASE WHEN target = 5000 AND rn = 1 THEN 1 END ) OVER () = 0 THEN 0 ELSE 1 END AS rnk
FROM (
SELECT t.*,
ROW_NUMBER() OVER ( PARTITION BY target ORDER BY name ) AS rn
FROM table_name t
);
Вывод:
NAME | TARGET | RNK :------ | -----: | --: John | 2500 | -2 Hopkins | 4000 | -1 Bob | 5000 | 0 Smith | 5000 | 0 Janet | 5100 | 1 Paul | 5100 | 1 Gracia | 5200 | 2
дб <> рабочий пример здесь
Этот запрос имеет некоторые трудности в случае, если в таблице нет строки с TARGET=5000.
Аналогично следует рассмотреть случай с более чем одной строкой с TARGET=5000.
Это достойная транскрипция вашего требования.
У вас есть три разных случая target, поэтому вы настроили case с тремя ветвями, вычисляющими RANK.
Вы можете использовать обычную агрегированную аналитическую функцию RANK, но технически вы должны использовать PARTITION BY в своих трех случаях.
select NAME, TARGET,
case when target = 5000 then 0
when target > 5000 then RANK()
over (partition by case when target > 5000 then 1 when target < 5000 then -1 else 0 end
order by target)
when target < 5000 then - RANK()
over (partition by case when target > 5000 then 1 when target < 5000 then -1 else 0 end
order by - target)
end as rank
from tab;
NAME TARGET RANK
------- ---------- ----------
John 2500 -2
Hopkins 4000 -1
Paul 5000 0
Gracia 5200 1
Похоже, вы могли бы разделить запрос ранжирования на два, ранжируя население < 5000 и население > 5000 отдельно.