从表中选择小于表元素的下一个值

时间:2015-07-24 23:18:49

标签: sql-server sql-server-2012 cursor common-table-expression priority-queue

我正在使用SQL Server 2012.我正在编写一个加权优先级队列,我正在尝试根据作为表格给出的最大权重从队列中提取项目。

因此,我将获得一个类似于以下内容的表格,指定要拉出的项目数量和最大权重值。我想首先拉出较大的物品,然后再缩小较小的物品。

选择表

╔════════╦═══════════╗
║ weight ║ numValues ║
╠════════╬═══════════╣
║      1 ║         1 ║
║      2 ║         0 ║
║      3 ║         3 ║
╚════════╩═══════════╝

另一张看起来像这样的表

项目表

╔══════╦══════╦════════╗
║ item ║ val  ║ weight ║
╠══════╬══════╬════════╣
║    1 ║ fish ║      1 ║
║    2 ║ goat ║      1 ║
║    3 ║ cat  ║      1 ║
║    4 ║ duck ║      3 ║
║    5 ║ pig  ║      2 ║
╚══════╩══════╩════════╝

我需要的是从选择表中选择适合每个类别的权重最大的值。

我希望我的结果看起来像这样

结果

╔══════╦══════╦════════╗
║ item ║ val  ║ weight ║
╠══════╬══════╬════════╣
║    1 ║ fish ║      1 ║
║    2 ║ goat ║      1 ║
║    4 ║ duck ║      3 ║
║    5 ║ pig  ║      2 ║
╚══════╩══════╩════════╝

鸭子,猪和鱼满足3的重量值,山羊满足1个要求的重量。

希望这是有道理的。

我知道我可以用游标做这样的事情,但是这看起来似乎很慢而且有点过分。我认为可以用CTE做到这一点,但我不确定如何处理它。

感谢您的帮助。

修改解决方案

使用Jonathan的解决方案作为跳跃点,这是我精心制作的野兽。我认为它应该是“好的”,但可能不那么快。

declare @selection TABLE 
    ([item] int, [val] varchar(4), [weight] int)
;

INSERT INTO @selection
    ([item], [val], [weight])
VALUES
    (1, 'fish', 1),
    (2, 'goat', 1),
    (3, 'cat', 1),
    (4, 'duck', 3),
    (5, 'pig', 2)
;
 declare @item TABLE 
    ([weight] int, [numValues] int)

INSERT INTO @item
    ([weight], [numValues])
VALUES
    (1, 1),
    (2, 0),
    (3, 3)

declare @potentialValues TABLE([item] int, [val] varchar(4), [weight] int, queueWeight int, [rn] int)
declare @maxRows INT = (SELECT SUM(numValues) FROM @item)
declare @largestQueueItem INT = (SELECT MAX(weight) from @item where numValues > 0)

;with CTE AS (
    Select 
        s.[weight] as itemWeight, 
        i.[weight] as queueWeight, 
        item, 
        val, 
        ROW_NUMBER() OVER (PARTITION BY i.weight ORDER BY s.weight desc) AS RN
    from @selection s
    FULL OUTER JOIN @item i ON s.weight <= i.weight
    where i.numValues > 0)
insert into @potentialValues ([item], [val], [weight], queueWeight, [rn])
select item, val, itemweight, queueWeight, rn from CTE
Where rn <= @maxRows


Declare @currentQueueItemSize INT = @largestQueueItem
while (@currentQueueItemSize > 0)
BEGIN
  DECLARE @count INT = (SELECT numValues from @item where weight = @currentQueueItemSize)
  ; WITH T
     AS (SELECT p.*
         FROM @potentialValues p
         WHERE p.queueWeight = @currentQueueItemSize
         ORDER BY p.rn
         OFFSET @count ROWS)
    DELETE FROM T

    DELETE p FROM @potentialValues p
    INNER JOIN @potentialValues pp 
    ON pp.item = p.item AND p.queueWeight < @currentQueueItemSize AND pp.queueWeight = @currentQueueItemSize

  SET @currentQueueItemSize = @currentQueueItemSize - 1
END

select item, val, weight from @potentialValues order by item

好消息是我没有使用光标。坏消息是我使用带有cte和delete语句的while循环来正确配对表。

任何方式可以在1次传球中获得2次传球?

2 个答案:

答案 0 :(得分:1)

declare @selection TABLE 
    ([item] int, [val] varchar(4), [weight] int)
;

INSERT INTO @selection
    ([item], [val], [weight])
VALUES
    (1, 'fish', 1),
    (2, 'goat', 1),
    (3, 'cat', 1),
    (4, 'duck', 3),
    (5, 'pig', 2)
;
 declare @item TABLE 
    ([weight] int, [numValues] int)

INSERT INTO @item
    ([weight], [numValues])
VALUES
    (1, 1),
    (2, 0),
    (3, 3)


;with CTE AS (
select T.item,t.val,tt.weight,ROW_NUMBER()OVER(PARTITION BY TT.weight ORDER BY TT.weight)RN from @selection T 
FULL OUTER  JOIN @item TT
ON T.weight = TT.weight)
SELECT ITEM,
VAL,
COALESCE(weight,ROW_NUMBER()over(PARTITION BY weight ORDER BY item)+1,0)
 FROM CTE where ITEM IS NOT NULL AND RN <= 2

答案 1 :(得分:0)

解决方案

使用Jonathan的解决方案作为跳跃点,这是我精心制作的野兽。我认为它应该是“好的”,但可能不那么快。

declare @selection TABLE 
    ([item] int, [val] varchar(4), [weight] int)
;

INSERT INTO @selection
    ([item], [val], [weight])
VALUES
    (1, 'fish', 1),
    (2, 'goat', 1),
    (3, 'cat', 1),
    (4, 'duck', 3),
    (5, 'pig', 2)
;
 declare @item TABLE 
    ([weight] int, [numValues] int)

INSERT INTO @item
    ([weight], [numValues])
VALUES
    (1, 1),
    (2, 0),
    (3, 3)

declare @potentialValues TABLE([item] int, [val] varchar(4), [weight] int, queueWeight int, [rn] int)
declare @maxRows INT = (SELECT SUM(numValues) FROM @item)
declare @largestQueueItem INT = (SELECT MAX(weight) from @item where numValues > 0)

;with CTE AS (
    Select 
        s.[weight] as itemWeight, 
        i.[weight] as queueWeight, 
        item, 
        val, 
        ROW_NUMBER() OVER (PARTITION BY i.weight ORDER BY s.weight desc) AS RN
    from @selection s
    FULL OUTER JOIN @item i ON s.weight <= i.weight
    where i.numValues > 0)
insert into @potentialValues ([item], [val], [weight], queueWeight, [rn])
select item, val, itemweight, queueWeight, rn from CTE
Where rn <= @maxRows


Declare @currentQueueItemSize INT = @largestQueueItem
while (@currentQueueItemSize > 0)
BEGIN
  DECLARE @count INT = (SELECT numValues from @item where weight = @currentQueueItemSize)
  ; WITH T
     AS (SELECT p.*
         FROM @potentialValues p
         WHERE p.queueWeight = @currentQueueItemSize
         ORDER BY p.rn
         OFFSET @count ROWS)
    DELETE FROM T

    DELETE p FROM @potentialValues p
    INNER JOIN @potentialValues pp 
    ON pp.item = p.item AND p.queueWeight < @currentQueueItemSize AND pp.queueWeight = @currentQueueItemSize

  SET @currentQueueItemSize = @currentQueueItemSize - 1
END

select item, val, weight from @potentialValues order by item

好消息是我没有使用光标。坏消息是我使用带有cte和delete语句的while循环来正确配对表。

任何方式可以在1次传球中获得2次传球?