1

表 Item 有一个字段 NUM_UNITS。

id      num_units
--      ---------
1       2
2       4
3       1
4       7

对于上述行集,sum(num_units) = (2 + 4 + 1 + 7) = 14。

我想编写允许我将 14 更改为 14 - N(即减少)的 SQL 代码,N 是任意数字。

N = 3:row1:num_units=0,row3:num_units=0。

N = 4:row2:num_units=0

N = 5:row2:num_units=0,row3:num_units=0

N = 6:row2:num_units=0,row1:num_units=0,row3:num_units=0

N = 7:row2:num_units=0,row1:num_units=0,row3:num_units=0,row4:num_units=6

目标是实现更新,使 SUM(NUM_UNITS) 减少 N 行。

这是我到目前为止提出的:我做出了一个函数,可以在选择最大的行和最小行的N2时测试与所需结果的距离。

ALTER FUNCTION F
(
    @GOAL INT,
    @P1 INT,
    @P2 INT
)
RETURNS INT
AS
BEGIN
    DECLARE @X INT = (
        SELECT SUM(NUM_UNITS)
        FROM (
            SELECT TOP(@P1) ID, NUM_UNITS
            FROM ITEM
            ORDER BY NUM_UNITS DESC
            UNION
            SELECT TOP(@P2) ID, NUM_UNITS
            FROM ITEM
            ORDER BY NUM_UNITS ASC
        ) J
    )
    RETURN @GOAL - @X
END
GO

现在的诀窍是连续调用它,并找到下一个最小的行以从中删除剩余部分。

综上所述,我对解决问题很感兴趣,而不是特别想这样做。这个逻辑让我想起了编写一个程序来做出改变。即,如果某物花费 X 并且收银员收到 Y,他/她如何选择钞票和硬币以最佳回报?

任何想法表示赞赏。对可行性和/或方向的建议表示赞赏。我可以用另一种编程语言做到这一点,但我想看看我是否可以用 SQL 做到这一点,部分是作为一种学习经验。

4

1 回答 1

1

让 N = 14

以下是算法运行前表 1 的全部内容。

在此处输入图像描述

以下是算法运行后Table1的全部内容。

在此处输入图像描述

此代码旨在在一个查询窗口/SP 中运行。

声明:

declare @N int = 14
declare @Remaining int = @N
declare @CurrentID int
declare @Current_Num_Units int

第 1 步:删除尽可能多的大记录

Declare MaxCursor CURSOR FAST_FORWARD FOR
select id, num_units from Table1 order by num_units desc, id

-- Step 1: Remove as many large records as possible
OPEN MaxCursor
FETCH NEXT FROM MaxCursor
INTO @CurrentID, @Current_Num_Units

WHILE @@FETCH_STATUS = 0
BEGIN

    if @Current_Num_Units <= @Remaining
    begin
        set @Remaining = @Remaining - @Current_Num_Units -- eliminate row and subtract from @Remaining
        update Table1 set num_units = 0 where ID = @CurrentID
    end
    else
    begin
        break -- exit loop if the this "next" record is too large to subtract from
    end

    -- grab the next record
    FETCH NEXT FROM MaxCursor
    INTO @CurrentID, @Current_Num_Units
END 

CLOSE MaxCursor
DEALLOCATE MaxCursor

第 2 步:删除尽可能多的小记录

-- Step2: Eliminate as many small records as possible
Declare MinCursor CURSOR FAST_FORWARD FOR
select id, num_units from Table1 where num_Units > 0 order by num_units, id  -- ascending instead of descending, j

Open MinCursor
FETCH NEXT FROM MinCursor
INTO @CurrentID, @Current_Num_Units

WHILE @@FETCH_STATUS = 0
BEGIN

    if @Current_Num_Units <= @Remaining
    begin
        set @Remaining = @Remaining - @Current_Num_Units -- eliminate row and subtract from @Remaining
        update Table1 set num_units = 0 where ID = @CurrentID
    end
    else
    begin
        break -- exit loop if the this "next" record is too large to subtract from
    end

    -- grab the next record
    FETCH NEXT FROM MinCursor
    INTO @CurrentID, @Current_Num_Units
End

CLOSE MinCursor
DEALLOCATE MinCursor

第 3 步:将下一个可用的最小记录减去余数

-- Step 3. Take the next record and subtract the remaining difference from its num_units
set @CurrentID = (select top 1 ID from Table1 where num_units > 0 order by num_units, id)

update Table1 set num_units = num_units - @Remaining where ID = @CurrentID

-- Now we have reduced by N, starting with the high records, then the min records, then we took the difference out of a remaining record.
select * from table1

笔记

  • 这不是好的 SQL 代码。SQL 最适合基于集合的操作——几乎没有这种情况发生。对于大型数据集,您几乎肯定更适合使用 C# 或 VB.NET 等标准语言执行此操作,然后将更改保存到数据库。
  • 上面的循环是用光标处理的。大多数时候,当有人认为他们需要一个游标来在 SQL 中做某事时,他们正在做一些更适合上面提到的标准编码环境的事情。
  • 做了一些假设,例如表中始终存在一些记录等。我只是想建立一个可以构建的框架。
于 2011-07-20T04:17:35.400 回答