Как использовать SQL для реализации логики, аналогичной внесению изменений? - PullRequest
1 голос
/ 20 июля 2011

Элемент таблицы имеет поле NUM_UNITS.

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

Для указанного набора строк сумма (num_units) = (2 + 4 + 1 + 7) = 14.

Я хотел бы написать код SQL, который позволяет мне изменить 14 на 14 - N (т.е. уменьшить), где 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

Теперь уловка состоит в том, чтобы вызывать это последовательно, а также находить следующую наименьшую строку для удаления остатка.

Все это говорит о том, что я заинтересован в решении проблемы, а не в том, чтобы делать это, в частности. Логика напоминает мне о написании программы для внесения изменений. То есть если что-то стоит X, а кассир получает Y, как он / она выбирает купюры и монеты для оптимального возврата?

Любые мысли приветствуются. Предложения по осуществимости и / или направления, чтобы пойти в приветствуются. Я мог бы сделать это на другом языке программирования, но я пытаюсь понять, смогу ли я сделать это на SQL, отчасти как опыт обучения.

1 Ответ

1 голос
/ 20 июля 2011

Пусть N = 14

Вот полное содержимое таблицы 1 до запуска алгоритма.

enter image description here

Вот полное содержимое таблицы 1 после запуска алгоритма.

enter image description here

Этот код предназначен для запуска в одном окне запроса / 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, он делает что-то, что лучше подходит для стандартной среды программирования, как упомянуто выше.
  • Было сделано несколько предположений, например, какие-то записи всегда существуют в таблице и т. Д. Я просто хотел заложить основу, на которой вы можете построить.
...