2014-09-25 4 views
0

Как рассчитать разницу между двумя строками в интервале дат?

Я пытаюсь сравнить данные из базы данных Access 2010 на основе интервала дат. Пример. У меня есть элементы из разных заказов на покупку, и я хочу сохранить историю доставки этого товара на склад. Таким образом, у моего заказа на поставку есть запрос на количество 10 материалов, например, и он может быть частично доставлен во многих поставках, и я хочу знать, как эта доставка менялась в интервале дат. Чтобы заполнить поле даты, используются следующие критерии: если элемент имел обновление в поле QtyPending, я копирую текущую строку, дезактивирую ее с помощью поля booelan, создавая новую запись с текущей датой обновления, обновляющей поле QtyPending, поэтому активная запись - это фактическое состояние элемента. Таким образом, у меня есть таблица, которая содержит информацию об этих элементах, как этого

 
    PO   POItem   QtyPending   Date   Active 
450000/09/2014  FALSE 
450000/09/2014  TRUE 
4500000122  30    5    03/09/2014  FALSE 
4500000122  30    1    04/09/2014  TRUE 

В этом примере для первого элемента, то это означает, что с даты 01/09 до 04/09 поле QtyPending не пострадало вариант, означающий, что поставщик не сделал мне никакой доставки, но с 01/09 по 05/08 он доставил мне 2 штуки. Для второго, с даты 03/09 до 04/09, поставщик доставил мне 4 штуки. Так, если бы я, чтобы делать запрос отчета от 02/09/2014 до 04/09/2014, ожидаемый выход таков:

 
    PO   POItem   QtyDelivered 
450000
4500000122  30     4 

И отчет от 31/08/2014 10/09/2014, будет иметь этот выход

 
    PO   POItem   QtyDelivered 
450000
4500000122  30     4 

Я не придумывая запроса, чтобы сделать этот отчет. Может кто-нибудь мне помочь?

+0

дата может пролеты принимать более двух записей? Что делает поле «Актив»? (Больше вопросов, чтобы следовать ... это не очень понятно!) – Smandoli

+0

Ваши требования звучат очень похоже на предыдущий вопрос [здесь] (http://stackoverflow.com/q/26046828/2144390). Проверьте ответы и посмотрите, поможет ли вам одна из них. ([Мой ответ] (http://stackoverflow.com/a/21033184/2144390) показывает, как использовать * self-join *, чтобы избежать циклов набора записей.) –

+0

Smandoli, активное поле, если FALSE, это говорит о том, что запись хранится только в исторических целях для запроса прошлых состояний элементов. И если активна TRUE, значит текущее состояние элементов, поэтому, если я хочу запросить только фактическое состояние элемента, единственное, что мне нужно сделать, это WHERE Active = TRUE –

ответ

0

Я придумал этот запрос, который решил проблему, это было не так просто

SELECT 
ItmDtIni.PO 
,ItmDtIni.POItem AS [PO Item] 
,ROUND(ItmDtIni.QtyPending - ItmDtEnd.QtyPending, 3) AS [Qty Delivered] 
,ROUND((ItmDtIni.QtyPending - ItmDtEnd.QtyPending) * ItmDtEnd.Price, 2) AS [Value delivered(US$)] 
//Filtering subqueries to bring only the items in the date interval to make a self join 
FROM (((SELECT 
     PO 
     ,POItem 
     ,QtyPending 
     ,MIN(Date) AS MinDate 
     FROM Item 
     WHERE Date BETWEEN FORMAT(begin_date, 'dd/mm/yyyy') AND FORMAT(end_date, 'dd/mm/yyyy') 
     GROUP BY 
     PO 
     ,POItem 
     ,QtyPending) AS ItmDtIni 
//Self join filtering to bring only items in the date interval with the previously filtered table 
INNER JOIN (SELECT 
      PO 
      ,POItem 
      ,QtyPending 
      ,Price 
      ,MAX(Date) AS MaxDate 
      FROM Item 
      WHERE Date BETWEEN FORMAT(begin_date, 'dd/mm/yyyy') AND FORMAT(end_date, 'dd/mm/yyyy') 
      GROUP BY 
      PO 
      ,POItem 
      ,QtyPending 
      ,Price) AS ItmDtEnd 
    ON ItmDtIni.PO = ItmDtEnd.PO 
    AND ItmDtIni.POItem = ItmDtEnd.POItem) 
INNER JOIN PO 
    ON ItmDtEnd.PO = PO.Numero) 
WHERE 
//Showing only items that had a variation in the date interval 
     ROUND(ItmDtIni.QtyPending - ItmDtEnd.QtyPending, 3) <> 0 
//Anchoring min date in the interval for each item found by the first subquery 
     AND ItmDtIni.MinDate = (SELECT MIN(Item.Date) 
           FROM Item 
           WHERE 
            ItmDtIni.PO = Item.PO 
           AND ItmDtIni.POItem = Item.POItem 
           AND Date BETWEEN FORMAT(begin_date, 'dd/mm/yyyy') AND FORMAT(end_date, 'dd/mm/yyyy')) 
//Anchoring max date in the interval for each item found by the second subquery 
    AND ItmDtEnd.MaxDate = (SELECT MAX(Item.Date) 
          FROM Item 
          WHERE 
           ItmDtEnd.PO = Item.PO 
          AND ItmDtEnd.POItem = Item.POItem 
          AND Date BETWEEN FORMAT(begin_date, 'dd/mm/yyyy') AND FORMAT(end_date, 'dd/mm/yyyy')) 
+0

Гораздо лучший способ сделать то же самое, что и в другом вопросе. После этой первой попытки написать запрос я подумал о лучшем способе сделать это. И вот оно: http://stackoverflow.com/q/26465285/2726538 –

0

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

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

Для вычисления этого значения вы можете либо использовать подзапрос, чтобы получить значение из предыдущей строки, либо левое соединение сделать то же самое. Когда у вас есть это значение, вы можете вычесть эти два, чтобы получить необходимую разницу; что позволяет использовать значение Null, если предыдущей строки нет. Когда у вас есть эти значения, вы можете теперь суммировать их, чтобы получить окончательный результат с помощью группы By. Обратите внимание, что для выполнения этих вычислений вам нужно иметь один или два уровня подзапроса. Первый запрос должен быть что-то вроде:

Select PO, POItem, QtyPending, (Select Top 1 QtyPending from MyTable T2 where T1.PO = T2.PO and T2.Date < T1.Date And (T2.Date between @Date1 and @Date2) Order by T2.Date Desc) as QtyPending2 from MyTable T1 Where T1.Date between @Date1 and @Date2) ... 

с этим, как ни другого подзапроса или как вид, вы можете вычислить нужную разницу путем сравнения значений QtyPending и QtyPending2; не забывая, что QtyPendin2 может быть Null. Остальные шаги легко сделать.

Обратите внимание, что приведенный выше пример для SQL-Server, возможно, вам придется немного изменить его для Access.В любом случае вы можете найти здесь множество примеров того, как сравнивать две строки в Access. Как уже отмечалось ранее, вы можете также использовать Left Join вместо подзапроса для сравнения ваших строк.

+0

Я подытожил требования. Я работаю с отчетом SAP, отчет ME2N, для отслеживания определенных заказов на поставку заказов. Я импортирую его в Excel, чтобы обрабатывать и отслеживать элементы с более высоким уровнем детализации. Таким образом, элемент может быть поставлен в дате и страдать от разворота из-за проблемы определенного типа. Если это произойдет, QtyPending переходит от 0 до 10, например, давая мне разницу в -10 в интервале дат. Так что просто SUM() QtyPending. Я бы не принимал эти отрицательные отличия, но мне важно знать, имел ли данный предмет разворот. –