我正在实施一个有多个优先级的排队系统 我想要一个可以返回X行的查询,每行优先级为至少 Y行。
例如:
假设队列有3个优先级(高,中和低),我希望每个优先级分别为3行,2行和1行。
如果表格如下:
-----------------
| Id | Priority |
-----------------
| 1 | High |
| 2 | High |
| 3 | High |
| 4 | High |
| 5 | Medium |
| 6 | Medium |
| 7 | Low |
-----------------
联合的三个简单查询将返回(1,2,3,5,6,7)。
SELECT TOP 3 Id FROM tbl WHERE Priority = 'High' UNION
SELECT TOP 2 Id FROM tbl WHERE Priority = 'Medium' UNION
SELECT TOP 1 Id FROM tbl WHERE Priority = 'Low'
但是,当表格中没有足够的特定优先级时会出现问题:
-----------------
| Id | Priority |
-----------------
| 1 | High |
| 2 | High |
| 3 | High |
| 4 | High |
| 5 | Medium |
| 6 | Low |
| 7 | Low |
-----------------
我想让它回来(1,2,3,4,5,6) 使用最高优先级填补空白(在这种情况下,使用第4个高行,因为没有足够的介质)。
是否存在可以容纳此问题的查询,或者我是否可以更好地在应用程序内部而不是在SQL级别进行过滤?
答案 0 :(得分:0)
您总是可以编写一个存储过程,在for循环中执行此操作(3次,每个优先级一个,从最低开始),并在每次迭代时动态调整下一次迭代中返回的值的数量(更高的优先级)如果这个中没有足够的。
动态我的意思是:
SELECT TOP (@count) * FROM SomeTable
如果在上一次迭代中有必要调整count
。
在应用程序逻辑中使用它的问题是你要么需要获取更多数据(是TOP
中使用的最大计数器的3倍),这样你就可以有足够的数据来填充你的插槽,或者你必须再次与您的数据库通信。
对于小数字,预防性的额外提取并不重要。我的偏好是将它放在存储过程中。
但是有很多变量在这里起作用:你获取的各个行的大小,你搜索的表的大小及其索引,程序体系结构,网络配置,计数器值,优先级数等等。等等。
答案 1 :(得分:0)
假设您使用的是SQL Server 2005或更高版本,嵌套CTE
和排名函数的组合应该能够解决问题:
;with A as
(
select top 3 * from tbl where priority = 'High'
),
A1 as
(
select id, priority from (
select * from A
union all
select top 2 id, priority from (select *,
ROW_NUMBER() over (order by case when priority = 'Medium' then 1 else 2 end) as ranker
from tbl where priority in ('High', 'Medium') and id
not in (select id from A))Z order by ranker asc
) X
),
A2 as (
select id, priority from (
select * from A1
union all
select top 1 id, priority from (select *,
ROW_NUMBER() over (order by case when priority = 'Low' then 1 else 2 end) as ranker
from tbl where priority in ('High', 'Low') and id
not in (select id from A1))Z order by ranker asc
) X
)
select * from A2
order by id
答案 2 :(得分:0)
此查询将为您提供最多3个低优先级,最多6个中等优先级和最多9个高优先级。如果没有足够的低优先级项来填充队列,则使用下一个最高优先级。
;WITH PriorityRanks AS (
SELECT
ID,
Priority,
ROW_NUMBER() OVER (PARTITION BY Priority ORDER BY ID ASC) as [Rank]
FROM PriorityQueue
)
, LowPriority AS (
SELECT
ID,
Priority,
ROW_NUMBER() OVER (ORDER BY ID ASC) as [Rank]
FROM PriorityRanks
WHERE Priority = 'Low'
AND [Rank] <= 3
)
, MediumPriority AS (
SELECT
ID,
Priority,
ROW_NUMBER() OVER (ORDER BY ID ASC) as [Rank]
FROM PriorityRanks pq
WHERE pq.Priority = 'Medium'
AND [Rank] <= 6
- (SELECT ISNULL(MAX([Rank]), 0) FROM LowPriority)
)
, HighPriority AS (
SELECT
ID,
Priority
FROM PriorityRanks pq
WHERE pq.Priority = 'High'
AND [Rank] <= 9
- (SELECT ISNULL(MAX([Rank]), 0) FROM LowPriority)
- (SELECT ISNULL(MAX([Rank]), 0) FROM MediumPriority)
)
SELECT ID, Priority FROM LowPriority
UNION ALL
SELECT ID, Priority FROM MediumPriority
UNION ALL
SELECT ID, Priority FROM HighPriority
编辑:对不起,我刚注意到你想要填写空白的最高优先级。不应该太难修改,但如果你有问题留下评论,我会帮你解决。
答案 3 :(得分:0)
我已经采用了基于CTE的方法,希望每一步都能显示我所遵循的思维过程:
declare @t table (Id int not null, Priority varchar(6) not null)
insert into @t (Id,Priority) values
(1,'High'),
(2,'High'),
(3,'High'),
(4,'High'),
(5,'Medium'),
(6,'Low'),
(7,'Low')
--We want 6 rows. We'd like to get 1 low, if available, and 2 mediums, if available
; with NumberedRows as (
select
Id,Priority,
ROW_NUMBER() OVER (PARTITION BY Priority ORDER BY Id) as rn,
CASE Priority WHEN 'High' then 1 WHEN 'Medium' THEN 2 ELSE 3 END as NumPri
from @t
), Selection as (
select Id, Priority, NumPri,
CASE
WHEN NumPri = 3 and rn <= 1 THEN 1
WHEN NumPri = 2 and rn <= 2 THEN 2
WHEN NumPri = 1 THEN 3
WHEN NumPri = 2 THEN 4
ELSE 5 --Low, rn>1
END as Preference
from NumberedRows
), Chosen as (
select top 6 * from Selection order by Preference
)
select * from Chosen order by NumPri,Id
(另外请注意,我的代码顶部的示例数据所需的格式与问题中的表格一样多 - 但它实际上可以在脚本中使用)
如果要选择的项目数量不同,则需要更改:
WHEN NumPri = 3 and rn <= 1 THEN 1
WHEN NumPri = 2 and rn <= 2 THEN 2
(更改rn
值)和:
select top 6 * from Selection order by Preference
(将其更改为总共需要很多人)
另请注意,您说您想要3个高优先级项并不重要 - 这无关紧要,因为如果没有足够的低优先级值可以找到高优先级项目。