Выберите список дат из заданного диапазона в Firebird - PullRequest
0 голосов
/ 05 февраля 2019

Я разрабатываю отчет, который возвращает BuyOrder, подлежащий оплате на будущей неделе.

Запрос, который я добавил ниже, возвращает PurchaseOrder из-за определенного Commodity, AmountDue и его DeliveryDate.

Очевидно, он возвращает только те PO_Dates, которые есть в таблице.Я хочу также включить даты, когда PO не ожидается, т. Е. Для этих ячеек будет ноль.

Для меня одна возможность - СЛЕДУТЬ СОЕДИНИТЬ набор данных с набором дат будущей недели в столбце Date, который в конечном итоге будетсделать результат нулевым, если не ожидается заказ на покупку.

В Firebird я не знаю, как выбрать список длинных дат недели, а затем использовать его в объединении.

SELECT
    PURCHASE_ORDER_DET.COMMODITYID AS COM_ID,
    PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('29.12.1899' AS DATE) as DLV_DATE,
    SUM(PURCHASE_ORDER_DET.REQQUANTITY) as DLV_DUE
FROM
    PURCHASE_ORDER_DET
LEFT JOIN PURCHASE_ORDER_HDR on PURCHASE_ORDER_HDR.POH_ID = 
PURCHASE_ORDER_DET.POH_ID
WHERE 
    PURCHASE_ORDER_DET.COMMODITYID = 1
    AND PURCHASE_ORDER_HDR.STATUS in (0,1,2)
    AND PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('30.12.1899' AS TIMESTAMP) >= '3.01.2019'
    AND PURCHASE_ORDER_DET.DELIVERYDATE + CAST ('30.12.1899' AS TIMESTAMP) <= '9.01.2019'
    AND PURCHASE_ORDER_DET.DELETED is NULL
Group by 
    PURCHASE_ORDER_DET.COMMODITYID,
    PURCHASE_ORDER_DET.DELIVERYDATE

DataSet

COM_ID    DLV_DATE       DLV_DUE    
1         3.01.2019     50.000000
1         5.01.2019     10.000000

Ожидается

COM_ID    DLV_DATE       DLV_DUE    
1         3.01.2019     50.000000
1         4.01.2019     null
1         5.01.2019     10.000000
1         6.01.2019     null
1         7.01.2019     null
1         8.01.2019     null
1         9.01.2019     null

1 Ответ

0 голосов
/ 05 февраля 2019

Игнорирование вашего нечетного использования типов данных *, есть несколько возможных решений:

  1. Используйте таблицу «календарь», которая содержит даты, и правое соединение с этой таблицей (или левое соединение с этой таблицей),Недостатком, конечно, является необходимость заполнения этой таблицы (но это разовые затраты).
  2. Используйте выбираемую хранимую процедуру, чтобы создать диапазон дат и присоединиться к нему.
  3. Создатьдиапазон в рекурсивном общем табличном выражении в самом запросе

Вариант 1 довольно понятен.

Вариант 2 будет выглядеть примерно так:

CREATE OR ALTER PROCEDURE date_range(startdate date, enddate date)
  RETURNS (dateval date)
AS
BEGIN
    dateval = startdate;
    while (dateval <= enddate) do
    BEGIN
       suspend;
       dateval = dateval + 1;
    END
END

И затем используйте это в своем запросе, например:

select date_range.dateval, ...
from date_range(date'2019-01-03', date'2019-01-09') -- use date_range(?, ?) for parameters
left join ...
  on date_range.dateval = ...

Вариант 3 будет выглядеть примерно так:

WITH RECURSIVE date_range AS (
    SELECT date'2019-01-03' dateval  -- start date, use cast(? as date) if you need a parameter
    FROM rdb$database
    UNION ALL 
    SELECT dateval + 1
    FROM date_range
    WHERE dateval < date'2019-01-09' -- end date use ? if you need a parameter
)
SELECT *
FROM date_range
LEFT JOIN ... 
  ON date_range.dateval = ... 

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


*: я бы посоветовал вам начать использовать DATE вместо того, что выглядит как число дней с 30-12-1899.Это избавляет от необходимости делать неловкие вычисления, как вы делаете сейчас.Если вам нужно это количество дней, то вы можете, например, использовать datediff(DAY FROM date'1899-12-30' TO somedatevalue) или somedatevalue - date'1899-12-30' для преобразования даты в это числовое значение.

...