创建oracle函数的问题

Issue in creating oracle function

我创建了一个需要输入月份 (11) 和年份 (2018) 的函数,它将 return 下个月的第一个星期日。

Create or replace function get_effectivedate (par_Month in int,Par_Year in int);

Return date

Is 

    startDate varchar;
    edate date;

    begin
    startDate := par_Month+Par_Year;
    edate     := select  next_day(add_months(trunc(startDate, 'MM'), 1), 'Sunday') as EffectiveDate from dual; 

     return edate;
     end;

这个函数在 运行 时提示我,它在编译时抛出一些错误。

错误:

LINE/COL  ERROR
--------- -------------------------------------------------------------
7/1       PLS-00103: Encountered the symbol "BEGIN" when expecting one of the following:     := . ( @ % ; not null range with default character The symbol ";" was substituted for "BEGIN" to continue. 
9/14      PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:     ( - + case mod new not null <an identifier>    <a double-quoted delimited-identifier> <a bind variable>    continue avg count current exists max min prior sql stddev    sum variance execute forall merge time timestamp interval    date <a string literal with character set specification>    <a number> <a single-quoted SQL string> pipe    <an alternatively-quoted string literal with character set specification>    <an alternat
11/2      PLS-00103: Encountered the symbol "RETURN" 
Errors: check compiler log

Function GET_EFFECTIVEDATE compiled

LINE/COL  ERROR
--------- -------------------------------------------------------------
7/1       PLS-00103: Encountered the symbol "BEGIN" when expecting one of the following:     := . ( @ % ; not null range with default character The symbol ";" was substituted for "BEGIN" to continue. 
9/14      PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:     ( - + case mod new not null <an identifier>    <a double-quoted delimited-identifier> <a bind variable>    continue avg count current exists max min prior sql stddev    sum variance execute forall merge time timestamp interval    date <a string literal with character set specification>    <a number> <a single-quoted SQL string> pipe    <an alternatively-quoted string literal with character set specification>    <an alternat
11/2      PLS-00103: Encountered the symbol "RETURN" 
Errors: check compiler log

Function GET_EFFECTIVEDATE compiled

LINE/COL  ERROR
--------- -------------------------------------------------------------
7/1       PLS-00103: Encountered the symbol "BEGIN" when expecting one of the following:     := . ( @ % ; not null range with default character The symbol ";" was substituted for "BEGIN" to continue. 
9/14      PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:     ( - + case mod new not null <an identifier>    <a double-quoted delimited-identifier> <a bind variable>    continue avg count current exists max min prior sql stddev    sum variance execute forall merge time timestamp interval    date <a string literal with character set specification>    <a number> <a single-quoted SQL string> pipe    <an alternatively-quoted string literal with character set specification>    <an alternat
11/2      PLS-00103: Encountered the symbol "RETURN" 
Errors: check compiler log

你想调用 startDate 变量,所以删除 & 符号,

& 是一个符号 for prompting user's input。将您的代码行更改为:

edate     := select  next_day(add_months(trunc(startDate, 'MM'), 1), 'Sunday') as EffectiveDate from dual; 

&value is a user entered parameter

您有几个问题:

  • 你在 create ... 行有一个尾随分号;
  • 您正在使用替换变量(由 & 表示)- 系统会提示您这样做;
  • 您使用 int 而不是 number 作为参数类型(不是真正的错误,但通常使用本机类型更好);
  • 你的 startdate 局部变量被声明为 varchar 而不是 varchar2,这有效但不鼓励,但无论哪种方式你都必须为字符串指定最大长度(即startdate varchar2(6););
  • 您将这两个数字与 + 相加,并将其结果(即 2029)放入字符串变量;
  • 您正在使用该字符串变量 - 如果您忽略 & - 作为数字而不是日期,那么 trunc 将不起作用;
  • 如果你已经过了这个月的第一天,那么 trunc 就毫无意义了。

你可以很简单地做到这一点:

create or replace function get_effectivedate (par_month in number, par_year in number)
return date
is 
begin
  return next_day(
    add_months(
      -- converts the two number to a single string, and then converts that to a date
      to_date(to_char(par_year, 'FM0000') || to_char(par_month, 'FM00'), 'YYYYMM'),
      1),
    'Sunday');
end;
/

然后将其命名为:

select get_effectivedate(11, 2018) from dual;

GET_EFFECT
----------
2018-12-02

或今年所有月份通过 CTE:

with cte (year, month) as (
  select 2018, level from dual connect by level <= 12
)
select year, month, get_effectivedate(month, year)
from cte;

      YEAR      MONTH GET_EFFECT
---------- ---------- ----------
      2018          1 2018-02-04
      2018          2 2018-03-04
      2018          3 2018-04-08
      2018          4 2018-05-06
      2018          5 2018-06-03
      2018          6 2018-07-08
      2018          7 2018-08-05
      2018          8 2018-09-02
      2018          9 2018-10-07
      2018         10 2018-11-04
      2018         11 2018-12-02
      2018         12 2019-01-06

将两个参数显式转换为字符串可以让您控制前导零,因此您可以确定传入 1 和 234 将转换为字符串 '01''0234',因此当它们连接起来形成 '023401',以匹配格式模型。

如果您没有明确指定天数,那么 to_date() 会为您提供该月的第一天。


如果一个月的第一天本身就是星期天,上面的函数会得到第二个星期日,你在评论中说这是你真正想要的。 (通知2018-07-08)。如果您确实只想要第一个星期日而不需要进一步调整,您可以:

  return next_day(
    add_months(
      -- converts the two numbers to a single string, and then converts that to a date
      to_date(to_char(par_year, 'FM0000') || to_char(par_month, 'FM00'), 'YYYYMM'),
      1) - 1, -- find first day of next month, then go back one day to last day of this month
    'Sunday'); -- find next Sunday from that day

或更简单地说:

  return next_day(
    last_day(
      -- converts the two numbers to a single string, and then converts that to a date
      to_date(to_char(par_year, 'FM0000') || to_char(par_month, 'FM00'), 'YYYYMM')
      ), -- find last day of this month
    'Sunday'); -- find next Sunday from that day

两者都给出L

      YEAR      MONTH GET_EFFECT
---------- ---------- ----------
      2018          1 2018-02-04
      2018          2 2018-03-04
      2018          3 2018-04-01
      2018          4 2018-05-06
      2018          5 2018-06-03
      2018          6 2018-07-01
      2018          7 2018-08-05
      2018          8 2018-09-02
      2018          9 2018-10-07
      2018         10 2018-11-04
      2018         11 2018-12-02
      2018         12 2019-01-06