如何在select语句中使用用户定义的函数作为游标以实现结果?

时间:2020-02-07 11:50:13

标签: sql oracle plsql

我有下面的游标:

OPEN p_results FOR
      SELECT distinct S02.CONVERSATION_ID, S02.INTERNAL_USI, S02.PRN,  S02.CRN, 
             S01.EARLIEST_B_DATE "Bank Date", S01.EARLIEST_B_NARRATIVE||'DSEC' "BN", 
             abs(S01.TOTAL_B_AMOUNT - S02.TOTAL_C_AMOUNT) "GA", 
             decode(S01.EXTRA_FLAG_4,1,'OP',2,'UP') "Batch Status", 
             (case 
                when S01.latest_b_date > S02.latest_c_date then S01.latest_b_date 
                else S02.latest_c_date 
              end ) Effective_Date, S02.ICC_ID,  
             S02.MATCH_METHOD,
             S01.TOTAL_B_AMOUNT                     
      FROM TTPAY S01,   
           TTCONT S02    
      Where S01.EXTRA_FLAG_4 in (1,2) 
      and   S02.EXTRA_FLAG_4 in (1,2) 
      and   S01.Match_no = 0 
      and   S02.Match_no = 0 
      and S01.PRN = S02.PRN;

对于这段代码,我必须进行以下更改:如果以下结果中的生效日期是假日或周末,那么我需要添加+1,而星期六需要+2,否则我将使用结果在下面的代码中以有效日期返回。

(case 
        when S01.latest_b_date > S02.latest_c_date then S01.latest_b_date 
        else S02.latest_c_date 
      end ) Effective_Date

我有一个名为WORKING_DAY的函数,该函数以一个日期作为参数,并在检查假期日历和周末后返回布尔值,分别为TRUEFALSE

如何在上面的代码片段中有效使用该功能以获取所需的日期?

5 个答案:

答案 0 :(得分:0)

我有一个名为WORKING_DAY的函数,该函数以一个日期作为参数,并在检查假期日历和周末后返回布尔值,分别为TRUEFALSE

如何在上面的代码片段中有效使用该功能以获取所需的日期?

您不能在SQL语句中使用它,因为BOOLEANPL/SQL data type,并且在SQL中不存在。

如果要在SQL中返回真实值,则返回文字(即0 / 1'Y' / 'N'等),并进行比较,如果您需要。

因此,您应将函数从将BOOLEAN(仅PL / SQL)数据类型返回为:

CREATE FUNCTION is_working_day(
  dt IN DATE
) RETURN NUMBER
IS
  is_working_day BOOLEAN;
BEGIN
  -- do stuff.

  IF is_working_day THEN
    RETURN 1;
  ELSE
    RETURN 0;
  END IF;
END;
/

更新

更改功能后,如何在该代码段中使用该功能以获取所需的有效日期?

您不能使用该功能来确定需要添加多少天。该函数返回真实值,因此,除非您要连续第二天进行迭代并测试是否为工作日(在SQL中很难做到),否则您当前的返回真实值的方法将无效。

相反,将逻辑移到一个函数中,该函数会告诉您要添加多少天:

CREATE FUNCTION days_until_next_working_day(
  dt IN DATE
) RETURN INTEGER
IS
  current_day DATE := dt;
BEGIN
  WHILE NOT is_working_day( current_day ) LOOP
    current_day := current_day + INTERVAL '1' DAY;
  END LOOP;
  RETURN current_day - dt;
END;
/

(可能有一种更有效的方式来编写此代码,以计算下一个工作日,而不是在每个连续的工作日中进行迭代,但是我们不知道您如何确定假期等,因此我们可以对其进行优化。)

然后只需将返回值添加到您的SQL查询中即可跳过这几天。

答案 1 :(得分:0)

在这种情况下,您拥有的功能几乎没有用。您必须知道有效日期是否为

  • 星期六或
  • 星期天或
  • 假日或
  • 这些都不是

因此,如果它返回TRUE或FALSE,则无法对其进行任何操作。您需要一个新函数,该函数将返回VARCHAR2,例如sunsatholnull(代表一个工作日)。然后,您可以在编写的查询中使用它。怎么样?一种简单的方法-无需使用触摸代码即可-将其用作嵌入式视图:

   OPEN p_results FOR
      SELECT conversation_id,
             CASE                                                 --> this is where you decide
                WHEN working_day_vc (effective_date) = 'sat'      --> whether day is SAT/SUN/HOLIDAY
                THEN
                   effective_date + 2
                WHEN working_day_vc (effective_date) IN ('sun', 'hol')
                THEN
                   effective_date + 1
                ELSE
                   effective_date
             END
                effective_date
        FROM (
              -- this is your current query
              SELECT DISTINCT
                     S02.CONVERSATION_ID,
                     S02.INTERNAL_USI,
                     S02.PRN,
                     S02.CRN,
                     S01.EARLIEST_B_DATE "Bank Date",
                     S01.EARLIEST_B_NARRATIVE || 'DSEC' "BN",
                     ABS (S01.TOTAL_B_AMOUNT - S02.TOTAL_C_AMOUNT) "GA",
                     DECODE (S01.EXTRA_FLAG_4,  1, 'OP',  2, 'UP')
                        "Batch Status",
                     (CASE
                         WHEN S01.latest_b_date > S02.latest_c_date
                         THEN
                            S01.latest_b_date
                         ELSE
                            S02.latest_c_date
                      END)
                        Effective_Date,
                     S02.ICC_ID,
                     S02.MATCH_METHOD,
                     S01.TOTAL_B_AMOUNT
                FROM TTPAY S01, TTCONT S02
               WHERE     S01.EXTRA_FLAG_4 IN (1, 2)
                     AND S02.EXTRA_FLAG_4 IN (1, 2)
                     AND S01.Match_no = 0
                     AND S02.Match_no = 0
                     AND S01.PRN = S02.PRN);

答案 2 :(得分:0)

如果这是某种报告,并且在sql中执行。例如

with
  function wf_WORKING_DAY(p_Date date) return number is
  begin
    return case when WORKING_DAY(p_Date) then 1 else 0 end;
  end;
select wf_WORKING_DAY(date'2020-01-01')
  from dual
/

答案 3 :(得分:0)

非常感谢@MT0 在这方面的帮助。

我写在下面以减轻要求。

CREATE OR REPLACE PACKAGE ACTUAL_DATE
IS
    FUNCTION IS_WORKING_DAY (IN_DATE IN DATE) RETURN BOOLEAN; 
    FUNCTION DAYS_UNTIL_NEXT_WORKING_DAY(IN_DATE IN DATE) RETURN NUMBER;
    FUNCTION GET_NEXT_BUSINESS_DAY(IN_DATE IN DATE) RETURN DATE;
END ACTUAL_DATE;
/
CREATE OR REPLACE PACKAGE BODY ACTUAL_DATE
IS
FUNCTION IS_WORKING_DAY (IN_DATE DATE) RETURN BOOLEAN
  IS
   HOLIDAYCOUNTER NUMBER :=0;
   DAYNUM POSITIVE;
  BEGIN
    -- CHECK DATE AGAINST NTBS_HOLIDAYS TABLE
    HOLIDAYCOUNTER :=0;
    SELECT COUNT(*) INTO HOLIDAYCOUNTER
    FROM HOLIDAY_CALENDAR
    WHERE HOLIDAY_DATE = TO_DATE(IN_DATE);  
    
    -- DETERMINE IF A WEEKDAY
    DAYNUM := TO_CHAR( IN_DATE, 'd');

    -- RETURN TRUE (1) ONLY IF A WEEKDAY AND NOT A NSW PPUBLIC HOLIDAY
    IF DAYNUM BETWEEN 2 AND 6 AND HOLIDAYCOUNTER = 0 THEN 
       RETURN TRUE;
    ELSE
       RETURN FALSE;
    END IF;
END IS_WORKING_DAY;

-- DETERMINE NO. OF DAYS TO ACHIEVE NEXT WORK DAY FOR GIVEN DATE
FUNCTION DAYS_UNTIL_NEXT_WORKING_DAY(
  IN_DATE IN DATE
) RETURN NUMBER
IS
  CURRENT_DAY DATE := IN_DATE;
  NUMBERS_OF_DAY NUMBER;
BEGIN
  WHILE NOT IS_WORKING_DAY( CURRENT_DAY ) LOOP
    CURRENT_DAY := CURRENT_DAY + INTERVAL '1' DAY;
  END LOOP;
  NUMBERS_OF_DAY := CURRENT_DAY - IN_DATE;
  RETURN NUMBERS_OF_DAY;
END;

-- ADD NO.OF DAYS TO GIVEN DATE TO GET ACTUAL EFFECTIVE DATE
FUNCTION GET_NEXT_BUSINESS_DAY(IN_DATE IN DATE) RETURN DATE
    IS
        BUSINESSDAY DATE := IN_DATE;
        ACTUAL_BUSINESSDAY DATE;
    BEGIN
        IF (IN_DATE IS NULL)
        THEN
            RAISE_APPLICATION_ERROR
            (
                -20999,
                'null date passed to get_next_business_day'
            );
        END IF;

        ACTUAL_BUSINESSDAY := BUSINESSDAY + DAYS_UNTIL_NEXT_WORKING_DAY(IN_DATE);
            
        RETURN ACTUAL_BUSINESSDAY;
    END;
END ACTUAL_DATE;
/

答案 4 :(得分:0)

通过简化假设,您可以将此过程简化为单个查询。该假设是您可以定义最大连续关闭天数。根据这个假设,当给定一个营业日期时,您会生成一个可能的下一个营业日期列表,然后从该列表中选择并返回未包含在假期表中的最短日期。我选择了 14 天作为最大连续关闭天数。虽然查询本身并不过分复杂,但也不是很简单,因此我会将它隐藏在一个函数中。见domo

create or replace 
function next_business_day( current_business_date_in    date
                          , max_consecutive_days_closed_in integer default 14
                          )
   return date
as 
    l_next_business_day  date; 
begin 
    with day_list (possible_next_date) as 
    -- generate list of possible next business days within
    -- the defined consecutive days closed 
         (select possible_next_date 
            from (select current_business_date_in + level possible_next_date 
                    from dual 
                    connect by level < max_consecutive_days_closed_in
                 ) 
           where to_char(possible_next_date,'d') not in ('6','7')
         )
    -- next business date is the minimum date from the list 
    -- that is not in the holiday table. 
    select min(possible_next_date) 
      into l_next_business_day
      from day_list
     where possible_next_date not in 
           (select holiday_date 
              from holidays 
            union all 
            -- make sure there is a last holiday defined
            select date '9999-12-31' from dual
           );
           
    return l_next_business_day; 
end next_business_day;

要使用,如果我正确理解您的查询,只需将您的 case 语句替换为对该函数的调用。所以

(CASE
     WHEN S01.latest_b_date > S02.latest_c_date
     THEN
          S01.latest_b_date
      ELSE
          S02.latest_c_date
  END) Effective_Date,

变成

next_business_day(S01.latest_b_date) Effective_Date, 

注意:刚刚注意到这实际上是一个旧帖子,但我最初看到的是昨天。所以我想我会离开它。

相关问题