从Firebird中的给定范围中选择日期列表

时间:2019-02-05 15:51:51

标签: sql firebird gaps-and-islands

我正在设计一个报表,该报表将在未来一周返回PurchaseOrder到期。

我在下面添加的查询针对特定的PurchaseOrderCommodity及其AmountDue返回DeliveryDate

很显然,它仅返回表中的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

数据集

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 :(得分:0)

忽略数据类型的奇怪用法*,有几种可能的解决方案:

  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'将日期从日期转换为该数值。