1


0

SQLを使用して、変更を行うことに似たロジックを実装する方法

テーブル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行ずつ減少するように更新を行うことです。

これまでに思いついたのは次のとおりです。最大行のN1と最小行の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

ここでのコツは、これを連続して呼び出し、残りを削除するために次に小さい行を見つけることです。

とは言うものの、私は問題を解決することに興味があり、特にこの方法でそれを行うことに興味はありません。 ロジックは、変更を加えるためのプログラムを書くことを思い出させます。 I.e. 何かがXかかり、キャッシャーがYを受け取った場合、どのようにして手形とコインを選択して最適に還元するのですか?

どんな考えでも感謝しています。 実行可能性および/または進むべき方向に関する提案を歓迎します。 別のプログラミング言語でこれを行うこともできますが、一部は学習体験として、SQLでできるかどうかを確認しようとしています。

1 Answer


1


  • N = 14とします*

アルゴリズムを実行する前のTable1の全内容を以下に示します。

image:https://i.stack.imgur.com/gKGdn.png [ここに画像の説明を入力]

アルゴリズム実行後のTable1の全内容は次のとおりです。

image:https://i.stack.imgur.com/zvT0b.png [ここに画像の説明を入力]

このコードは、1つのクエリウィンドウ/ 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などの標準言語でこれを行い、その後DBに変更を保存するのに適しています。

  • 上記のループはカーソルで処理されます。 ほとんどの場合 誰かがSQLで何かをするためにカーソルが必要だと思っている、彼らは上記の標準的なコーディング環境により適した何かをしている。

  • いくつかのレコードが常に存在するなど、いくつかの仮定が行われました テーブルなど 構築できるフレームワークを作成したかっただけです。