Обновление SQL - фиксировать каждую строку в порядке - PullRequest
3 голосов
/ 06 июня 2011

Доброе утро. Я сделаю все возможное, чтобы объяснить свой вопрос без публикации SQL (это 650 строк). Дайте мне знать, если потребуется дополнительная информация.

У нас есть собственная система выполнения заказов, которая распределяет запасы в режиме реального времени. Чтобы распределение работало должным образом, нам нужно знать, сколько ресурсов доступно каждый раз, когда пользователь спрашивает, над чем он должен работать (путем загрузки / перезагрузки списка задач). Данные будут выглядеть примерно так:

ID    ItemID    QtyOrdered    QtyAvailableAfterAllocation    ParentID
1     1234      5             500                            NULL
2     1234      15            485                            1
3     1234      10            475                            2

В настоящее время цикл while используется для установки столбца QtyAvailableAfterAllocation . Приведенный выше пример демонстрирует необходимость цикла. QtyAvailableAfterAllocation строки 2 зависит от значения QtyAvailableAfterAllocation строки 1. Строка 3 зависит от строки 2 и т. Д.

Это (очень) упрощенная версия логики. Это становится намного сложнее, когда вы принимаете во внимание наборы (группы предметов инвентаря, которые принадлежат одному родительскому предмету). Есть моменты, когда инвентарь не нужно выделять для предмета, потому что он существует внутри комплекта, который имеет достаточный инвентарь для выполнения заказа. Вот почему мы не можем сделать промежуточный итог. Кроме того, комплекты могут быть вложены в комплекты до N-го уровня. В этом и заключается проблема. При работе с большим количеством заказов с вложенными наборами производительность запроса очень низкая. Я считаю, что виноват цикл (тестирование доказало это). Итак, вот вопрос:

Можно ли зафиксировать обновление по одной строке за раз и в определенном порядке (без цикла), чтобы дочерние записи ниже могли получить доступ к обновленному столбцу (QtyAvailAfterOrder_AllocationScope) в родительской записи?

EDIT

Вот небольшая часть SQL. Это фактический цикл. Возможно, это поможет показать логику, необходимую для определения распределения каждой записи.

http://pastebin.com/VM9iasq9

Ответы [ 4 ]

1 голос
/ 06 июня 2011

Можете ли вы обмануть и сделать что-то подобное?

DECLARE @CurrentCount int
SELECT @CurrentCount = QtyAvailableAfterAllocation 
FROM blah 
WHERE <select the parent of the first row>

UPDATE blah
SET QtyAvailableAfterAllocation = @CurrentCount - QtyOrdered,
    @CurrentCount = @CurrentCount - QtyOrdered
WHERE <it is valid to deduct the count>

Это должно позволить вам сохранить обновление в соответствии с установленным набором и отсчитывать от начального количества.Суть проблемы здесь заключается в предложении WHERE.

Один из методов, который мы использовали, состоит в том, чтобы сгладить иерархию значений (в вашем случае, идея N-го набора) в таблицу, затем вы можете присоединитьсяна этот плоский стол.Сглаживание иерархии и одиночное объединение должны помочь устранить некоторые недостатки производительности.Возможно, используйте представление для выравнивания данных.

Извините, это не прямой ответ, а только идеи.

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

0 голосов
/ 11 июня 2011

Основываясь на комментариях / ответах выше и моей неспособности точно представить эту сложную проблему должным образом, я переписал обработку в C #. Используя PLINQ, я сократил время обработки с 15 секунд до 4. Спасибо всем, кто пытался помочь!

Если это неправильный способ закрыть вопрос, дайте мне знать (и дайте мне знать подходящий способ, чтобы я мог сделать это вместо этого).

0 голосов
/ 06 июня 2011

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

В основном вам необходимо выполнить следующие шаги:

  1. Получить таблицу доступных в настоящее времяколичество для каждого элемента.

  2. Получите промежуточные суммы из заказанного количества, подлежащего обработке.

  3. Получите QtyAvailableAfterAllocation для каждого элемента какрезультат вычитания его промежуточного итога из его доступного количества.

Вот пример решения:

/* sample data definition & initialisation */
DECLARE @LastQty TABLE (Item int, Qty int);
INSERT INTO @LastQty (Item, Qty) 
  SELECT 0123, 404 UNION ALL
  SELECT 1234, 505 UNION ALL
  SELECT 2345, 606 UNION ALL
  SELECT 3456, 707 UNION ALL
  SELECT 4567, 808 UNION ALL
  SELECT 5678, 909;
DECLARE @Orders TABLE (ID int, Item int, OrderedQty int);
INSERT INTO @Orders (ID, Item, OrderedQty)
  SELECT 1, 1234,  5 UNION ALL
  SELECT 2, 1234, 15 UNION ALL
  SELECT 3, 2345,  3 UNION ALL
  SELECT 4, 1234, 10 UNION ALL
  SELECT 5, 2345, 37 UNION ALL
  SELECT 6, 2345, 45 UNION ALL
  SELECT 7, 3456, 50 UNION ALL
  SELECT 8, 4567, 25 UNION ALL
  SELECT 9, 2345, 30;

/* the actuall query begins here */
WITH RankedOrders AS (
  SELECT
    *,
    rn = ROW_NUMBER() OVER (PARTITION BY Item ORDER BY ID)
  FROM @Orders
),
RunningOrderTotals AS (
  SELECT
    ID,
    Item,
    OrderedQty,
    RunningTotalQty = OrderedQty,
    rn
  FROM RankedOrders
  WHERE rn = 1
  UNION ALL
  SELECT
    o.ID,
    o.Item,
    o.OrderedQty,
    RunningTotalQty = r.RunningTotalQty + o.OrderedQty,
    o.rn
  FROM RankedOrders o
    INNER JOIN RunningOrderTotals r ON o.Item = r.Item AND o.rn = r.rn + 1
)
SELECT
  t.ID,
  t.Item,
  t.OrderedQty,
  QtyAvailableAfterAllocation = oh.Qty - t.RunningTotalQty
FROM RunningOrderTotals t
  INNER JOIN @LastQty oh ON t.Item = oh.Item
ORDER BY t.ID;

Примечание. Для целей моего примера я инициализировалтаблица доступных позиций (@LastQty) вручную.Тем не менее, вы, скорее всего, собираетесь извлечь его из ваших данных.

0 голосов
/ 06 июня 2011

Если у вас есть запросы, поставленные в очередь в некоторой структуре, вы не использовали бы оператор SQL для обработки очереди; «очередь» и «SQL» концептуально расходятся: SQL основан на множестве, а не процедурно.

Итак, забудьте об использовании запроса для управления поставленными в очередь запросами и обработайте очередь в процедуре, оборачивая каждую заявку в транзакцию:

        pseudo:
        WHILE REQUESTS_REMAIN_IN_QUEUE

             begin trans
                execute requisition SQL statements
             commit

        LOOP

Ваши заявки (упрощенно) могут выглядеть следующим образом:

           update inventory
           set QOH = QOH- {requested amount}
           where  partno = ? and QOH >= {requested amount}

           insert orderdetail
           (customer, orderheaderid, partno, requestedamount)
           values
           (custid, orderheaderid, partno, requested_amount)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...