Получите ближайшие привилегии на основе данной репутации - PullRequest
4 голосов
/ 19 марта 2019

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

Пожалуйста, найдите создание схемы привилегий и пример данных: https://rextester.com/KQZS91498

Исходя из заданного значения репутации, мне нужно найти ближайшие привилегии. то есть ближайшая привилегия, которая меньше заданной репутации и привилегии, которая может быть достигнута следующей.

Пример: если я предоставлю свою репутацию как 7276, мне понадобятся следующие привилегии в качестве вывода:

Id  Reputation  PrivilegeName
------------------------------------------
22  5000        approve tag wiki edits
23  10000       access to moderator tools

Для этого я использую следующий запрос.

DECLARE @MyReputation AS INT = 7276;

DECLARE @FromId AS INT = 0;
DECLARE @ToId AS INT = 0;

SELECT @FromId = MAX(Id) FROM @Privilege
WHERE Reputation > 0 AND Reputation <= @MyReputation;

SELECT @ToId = MIN(Id) FROM @Privilege
WHERE @MyReputation > 0 AND @MyReputation < Reputation;

SELECT Id, Reputation, PrivilegeName
FROM @Privilege
WHERE Id IN (@FromId, @ToId)
ORDER BY Id;

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

Пожалуйста, найдите исполняемый запрос для того же: https://rextester.com/PDEXOM92503.


Поскольку я использую агрегатную функцию для получения идентификаторов, она не работает, если репутация имеет более одной привилегии.

Здесь репутация 10 содержит две привилегии. И это возможно в будущем, у него может быть три или более привилегий.

Таким образом, ожидается, что, если я предоставлю свою репутацию как 10 или 13 , мне понадобится вывод:

Id  Reputation  PrivilegeName
----------------------------------------------
3   10          remove new user restrictions
4   10          create wiki posts
5   15          flag posts
6   15          vote up

И для ввода репутации 945 :

Id  Reputation  PrivilegeName
------------------------------------------
15  500         access review queues
16  1000        established user
17  1000        create gallery chat rooms

т. Е. Ближайшие возможные привилегии на основе заданной репутации.

Дело применимо к репутации, такой как 100 , 1000 , которая имеет более одной привилегии для одной и той же репутации.

Каков будет лучший способ оправдать мои ожидания, о которых говорилось выше?

Ответы [ 3 ]

2 голосов
/ 19 марта 2019

Вы также можете сделать это следующим образом, используя UNION ALL.

;WITH cte 
     AS (SELECT *, 
                reputation - @MyReputation DIFF 
         FROM   @Privilege) 
SELECT * 
FROM   cte 
WHERE  diff = (SELECT Max(diff) 
               FROM   cte 
               WHERE  diff <= 0) 
UNION ALL
SELECT * 
FROM   cte 
WHERE  diff = (SELECT Min(diff) 
               FROM   cte 
               WHERE  diff > 0) 

Онлайн-демонстрация

Выше можно также написать запросиспользуя CASE WHEN как показано ниже

;WITH cte 
     AS (SELECT *, 
                reputation - @MyReputation diff, 
                Max(CASE 
                      WHEN reputation - @MyReputation <= 0 THEN 
                      reputation - @MyReputation 
                    END) 
                  OVER()                   mind, 
                Min(CASE 
                      WHEN reputation - @MyReputation > 0 THEN 
                      reputation - @MyReputation 
                    END) 
                  OVER()                   maxd 
         FROM   @Privilege) 

SELECT Id,Reputation,PrivilegeName,DetailedDescription 
FROM   cte 
WHERE  diff in (mind,maxd) 
2 голосов
/ 19 марта 2019

Я бы использовал TOP(1) WITH TIES здесь.В подзапросах нет необходимости.

Вы не можете поместить эти два запроса с ORDER BY непосредственно в UNION ALL, поэтому я заключил их в CTE.

WITH
CTE1
AS
(
    SELECT TOP(1) WITH TIES
        *
    FROM @Privilege
    WHERE Reputation <= @MyReputation
    ORDER BY Reputation DESC
)
,CTE2
AS
(
    SELECT TOP(1) WITH TIES
        *
    FROM @Privilege
    WHERE Reputation > @MyReputation
    ORDER BY Reputation ASC
)
SELECT *
FROM CTE1

UNION ALL

SELECT *
FROM CTE2
;
2 голосов
/ 19 марта 2019

Вы можете использовать два условия ИЛИ НЕ СУЩЕСТВУЕТ, два фильтруют соответствующие записи:

SELECT *
FROM @Privilege t
WHERE 
    (t.reputation > @MyReputation AND NOT EXISTS (
        SELECT 1 
        FROM @Privilege t1 
        WHERE t1.reputation > @MyReputation AND t1.reputation < t.reputation
    ))
    OR (t.reputation <= @MyReputation AND  NOT EXISTS (
        SELECT 1 
        FROM @Privilege t2
        WHERE t2.reputation <= @MyReputation AND t2.reputation > t.reputation
    ));
...