我有一个业务案例,其中很少有案例。让我用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 包含两个特权。并且将来有可能拥有三个或更多特权。
因此,期望是,如果我提供的信誉输入为 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 之类的信誉,该信誉对于同一信誉具有多个特权。
达到上述期望的最佳方法是什么?
答案 0 :(得分: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
));
答案 1 :(得分: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)
答案 2 :(得分: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
;