使用ifs包含无限区间的日期范围

时间:2014-05-26 12:44:38

标签: sql oracle algorithm plsql overlap

我在Oracle中有一个函数,用于检查第一个日期是否包含第二个日期。它没有问题,因为如果[a, b] [x, y]包含x => a and y <= b and x <= y。适用于定义的start/end date 1start/end date 2。但现在我想修改它。如果给定'starting''ending'日期中的任何一个NULL,则应将其视为+-infinite

这是一个代码:

  FUNCTION CONTAINS(p_START_DATE_1 DATE, p_END_DATE_1 DATE,
   p_START_DATE_2 DATE, p_END_DATE_2 DATE) RETURN VARCHAR2 AS

   lv_RESULT VARCHAR2(1);
  BEGIN 
       lv_RESULT := 'N';      

       IF (/* milions of conditions here */) THEN
         lv_RESULT := 'Y';
       END IF;

       RETURN lv_RESULT;
  END CONTAINS;

例如:假设p_START_DATE_1NULL。在这种情况下代码:

SELECT MY_PACKAGE_SQL.CONTAINS(
       NULL,
       TO_DATE('01/12/2014', 'DD/MM/YYYY'),
       TO_DATE('01/02/2012', 'DD/MM/YYYY'),
       TO_DATE('01/05/2012', 'DD/MM/YYYY'))
FROM DUAL;

...应该返回Y,因为第一个日期范围是(-infinite, 01/12/2014],它包含[01/02/2012, 01/05/2012]

现在我的问题......我知道我可以使用额外的"IFs"来检查NULLs。我必须使用"IFs"(老板命令我这样做:S)。您是否知道如何仅使用"IFs"以“智能”方式进行操作?

2 个答案:

答案 0 :(得分:2)

使用可用例程处理NULL的一种方法是:

FUNCTION CONTAINS(a DATE, b DATE,  -- does interval [a, b] contain [x, y]?
                  x DATE, y DATE)
  RETURN VARCHAR2
AS
  lv_RESULT           VARCHAR2(1);
  dtPositiveInfinity  DATE := TO_DATE('31-DEC-9999 23:59:59',
                                      'DD-MON-YYYY HH24:MI:SS');
  dtNegativeInfinity  DATE := TO_DATE('01-JAN-4712 BC 00:00:00',
                                      'DD-MON-YYYY BC HH24:MI:SS');
BEGIN 
  lv_RESULT := 'N';      

  IF NVL(x, dtPositiveInfinity) >= NVL(a, dtNegativeInfinity) AND
     NVL(y, dtNegativeInfinity) <= NVL(b, dtPositiveInfinity) AND
     NVL(x, dtNegativeInfinity) <= NVL(y, dtPositiveInfinity)
  THEN
    lv_RESULT := 'Y';
  END IF;

  RETURN lv_RESULT;
END CONTAINS;

但是,你说你不能使用COALESCE,因此我推断你也不能使用NVL,在这种情况下可能会有以下情况:

FUNCTION CONTAINS(a DATE, b DATE,  -- does interval [a, b] contain [x, y]?
                  x DATE, y DATE)
  RETURN VARCHAR2
AS
  lv_RESULT           VARCHAR2(1);
  dtPositiveInfinity  DATE := TO_DATE('31-DEC-9999 23:59:59',
                                      'DD-MON-YYYY HH24:MI:SS');
  dtNegativeInfinity  DATE := TO_DATE('01-JAN-4712 BC 00:00:00',
                                      'DD-MON-YYYY BC HH24:MI:SS');
  aOrNegativeInfinity  DATE := a;
  bOrPositiveInfinity  DATE := b;
  xOrPositiveInfinity  DATE := x;
  yOrPositiveInfinity  DATE := y;
  xOrNegativeInfinity  DATE := x;
  yOrNegativeInfinity  DATE := y;
BEGIN 
  lv_RESULT := 'N';      

  IF a IS NULL THEN
    aOrNegativeInfinity := dtNegativeInfinity;
  END IF;

  IF b IS NULL THEN
    bOrPositiveInfinity := dtPositiveInfinity;
  END IF;

  IF x IS NULL THEN
    xOrPositiveInfinity := dtPositiveInfinity;
    xOrNegativeInfinity := dtNegativeInfinity;
  END IF;

  IF y IS NULL THEN
    yOrPositiveInfinity := dtPositiveInfinity;
    yOrNegativeInfinity := dtNegativeInfinity;
  END IF;

  IF xOrPositiveInfinity >= aOrNegativeInfinity   AND
     yOrNegativeInfinity <= bOrPositiveInfinity   AND
     xOrNegativeInfinity <= yOrPositiveInfinity
  THEN
    lv_RESULT := 'Y';
  END IF;

  RETURN lv_RESULT;
END CONTAINS;

如果使用多个IF语句存在问题,可能会接受以下内容:

FUNCTION CONTAINS(a DATE, b DATE,  -- does interval [a, b] contain [x, y]?
                  x DATE, y DATE)
  RETURN VARCHAR2
AS
  lv_RESULT           VARCHAR2(1);
  dtPositiveInfinity  DATE := TO_DATE('31-DEC-9999 23:59:59',
                                      'DD-MON-YYYY HH24:MI:SS');
  dtNegativeInfinity  DATE := TO_DATE('01-JAN-4712 BC 00:00:00',
                                      'DD-MON-YYYY BC HH24:MI:SS');
BEGIN 
  lv_RESULT := 'N';      

  IF CASE x WHEN NULL THEN dtPositiveInfinity ELSE x END >=
           CASE a WHEN NULL THEN dtNegativeInfinity ELSE a END AND
     CASE y WHEN NULL THEN dtNegativeInfinity ELSE y END <=
           CASE b WHEN NULL THEN dtPositiveInfinity ELSE b END AND
     CASE x WHEN NULL THEN dtNegativeInfinity ELSE x END <=
           CASE y WHEN NULL THEN dtPositiveInfinity ELSE y END
  THEN
    lv_RESULT := 'Y';
  END IF;

  RETURN lv_RESULT;
END CONTAINS;

未在动物身上进行测试 - 您将成为第一个!

分享并享受。

答案 1 :(得分:1)

这是逻辑,表示为case语句:

select (case when (p_START_DATE_1 is null or
                   p_START_DATE_1 >= p_START_DATE_2 or
                   p_START_DATE_1 is null and p_START_DATE_2 is null
                  ) and
                  (p_END_DATE_1 <= p_END_DATE_2) and
                  (p_START_DATE_1 <= p_END_DATE_1 or p_START_DATE_1 is null
              then 'Y'
              else 'N'
         end)

您也可以使用coalesce()更轻松地表达这一点:

select (case when coalesce(p.START_DATE_1, to_date('1900-01-01', 'YYYY-MM-DD')) > p_START_DATE_2 and
                  (p_END_DATE_1 <= p_END_DATE_2) and
                  coalesce(p.START_DATE_1, to_date('1900-01-01', 'YYYY-MM-DD')) <= p_END_DATE_2
             then 'Y'
             else 'N'
        end)

如果您希望其他列的其他逻辑为NULL(并且您有可用于NULL值的极端日期),则此方法会更容易。