4

我有一个业务场景,其中很少有边缘案例。让我用Stack Overflow Privileges数据来解释一下。

请找到权限架构创建和示例数据: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 。


由于我使用聚合函数来获取 Id,因此如果声誉具有多个特权,则它不起作用。

这里信誉10包含两个特权。并且有可能在未来,它可能拥有三个或更多的特权。

所以期望是,如果我提供我的声誉输入为1013,我需要输出为:

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

即,基于给定声誉输入的最接近的可能特权。

该案例适用于像1001000这样的声誉,它们对同一声誉具有多个特权。

实现上述期望的最佳方式是什么?

4

3 回答 3

2

您可以使用两个 ORed NOT EXISTS 条件来过滤相关记录:

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
    ));
于 2019-03-19T12:32:16.133 回答
2

您也可以使用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) 
于 2019-03-19T12:50:35.907 回答
2

我会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
;
于 2019-03-19T12:51:25.487 回答