0

我有一个包含数据的假期表

    HOLIDAYDA DESCRIPTION
   --------- --------------------
   19-JAN-11 to
   17-JAN-11 to
   10-JAN-11 new day

现在我想要一周的第一个工作日。IE:如果我通过“2011 年 1 月 12 日”作为输入,我希望 o/p 为 2011 年 1 月 11 日作为第一个工作日,因为 2011 年 1 月 10 日是假期。

这是我的代码:

create or replace procedure sample as
   l_dStartDay date;
   l_dHolidayDate date;
begin

    select trunc(to_date(sysdate),'Day') 
      into l_dStartday 
      from dual;

 dbms_output.put_line('first day of the week ');
 dbms_output.put_line(l_dStartDay);

 for i in 2..5 Loop
   select holidaydate 
     from holiday 
     into l_dHolidayDate 
    where holidaydate = (l_dStartDay + i);

  if(l_dHolidaydate is null) then
    dbms_output.put_line(l_dStartDay+i);
  end if;
exit;
end loop;
end;

我编译了上面的程序,但使用“过程创建时出现编译错误”。

新增:编译错误:

 LINE/COL ERROR
 -------- -----------------------------------------------------------------
 9/1      PL/SQL: SQL Statement ignored
 9/33     PL/SQL: ORA-00933: SQL command not properly ended

错误

BEGIN sample; END;
      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00905: object SYSTEM.SAMPLE is invalid
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

谁能告诉我错误的原因?如果可能的话告诉我解决方案?

4

3 回答 3

8

“我编译了上面的程序,但是用 Procedure created with compilation errors

如果您使用的是诸如 TOAD 或 SQL Developer 之类的 IDE,它会自动显示编译错误。否则,可以使用以下命令在 SQL*Plus 中访问它们:

SQL>    show errors

还有诸如USER_ERRORS之类的视图我们可以查询。

问题很可能是 SELECT 语句,因为 INTO 子句应该紧跟在投影之后:

   select holidaydate 
     into l_dHolidayDate 
     from holiday 
    where holidaydate = l_dStartDay + i);

请注意,这看起来也是错误的:

select trunc(to_date(sysdate),'Day') 

SYSDATE 已经是一个 DATE,尽管较新的 Oracle 版本倾向于更宽容地在 DATE 列上使用 TO_DATE。从日期截断时间元素时,不需要包含格式掩码,因为这是默认行为:

trunc(some_date_variable)

如果(比如说)我们想要一个月的第一天,我们只需要包含一个掩码:

trunc(some_date_variable, 'MON')

如果您想找到一周的第一天,可以这样做:

SQL> select
  2      trunc(to_date('01-DEC-2010', 'DD-MON-YYYY'), 'D') start_of_wk
  3  from dual
  4  /

START_OF_
---------
29-NOV-10

SQL>

请注意,一周的第一天取决于区域设置。在某些地区,一周的第一天是工作日(例如在英国的星期一),而在其他地区则不是(在美国,星期日是第一天)。所以可能需要添加一个偏移量。


解决编译错误后,您会发现一些运行时错误,可能与未处理的 NO_DATA_FOUND 异常有关。这是因为您的查找查询在找不到匹配记录时不会返回 NULL,它会失败。

这是一个简单的程序。它使用 SQL 解决方案,因为 SQL 是最有效的做事方式。内部查询使用 CONNECT BY 技巧来生成日期结果集。然后由 MINUS 集合运算符减少该值,该运算符将过滤掉该周范围内的任何假期。最后,外部查询返回查询的最早日期。

create or replace procedure get_first_working_day 
    ( p_tgt_date in date )
is
    l_st_day date := trunc(p_tgt_date, 'D');
    l_working_day date := trunc(p_tgt_date, 'D');
begin
    dbms_output.put_line('first day of week = '||l_st_day);

    select min(day_of_wk)
    into l_working_day
    from ( select l_st_day + (level-1) as day_of_wk
           from dual
           connect by level <= 5
           minus
           select holidaydate
           from hols
           where holidaydate between l_st_day and l_st_day + 4 );

    dbms_output.put_line('first working day of week = '||l_working_day
                        ||'::'|| to_char(l_working_day, 'DAY'));

end get_first_working_day;
/

鉴于此测试数据(反映了英国银行假期的拜占庭状态)...

SQL> select holidate from hols
  2  order by 1
  3  /

HOLIDAYDA
---------
25-DEC-10
26-DEC-10
27-DEC-10
28-DEC-10
01-JAN-11
03-JAN-11

6 rows selected.

SQL>

...这是实际操作的程序:

SQL> set serveroutput on size unlimited
SQL>
SQL> exec get_first_working_day (sysdate)
first day of week = 10-JAN-11
first working day of week = 10-JAN-11::MONDAY

PL/SQL procedure successfully completed.

SQL>
SQL> exec get_first_working_day (to_date( '04-JAN-2011', 'DD-MON-YYYY'))
first day of week = 03-JAN-11
first working day of week = 04-JAN-11::TUESDAY

PL/SQL procedure successfully completed.

SQL>
SQL> exec get_first_working_day (to_date( '01-JAN-2011', 'DD-MON-YYYY'))
first day of week = 27-DEC-10
first working day of week = 29-DEC-10::WEDNESDAY

PL/SQL procedure successfully completed.

SQL>

顺便说一句,这是非常糟糕的做法:

PLS-00905: object SYSTEM.SAMPLE is invalid

不要将内置的 SYS 或 SYSTEM 帐户用于您自己的工作。破坏东西的机会太大了。而是创建一个新的用户帐户。

于 2011-01-11T06:22:44.233 回答
0

我猜这条线

where holidaydate = l_dStartDay + i);

是错误的,因为它有一个)不应该在的地方。

于 2011-01-11T06:17:12.993 回答
0

除了已经提到的错误之外,请尝试删除“EXIT”子句,因为此循环将迭代固定次数。此外,尝试在结束块时指定块名称,如下所示:

    LOOP    
    ...
    END LOOP;
END ObjectName;

ObjectName 是您的顶级程序。在这里,它将是“样本”,所以:

    LOOP
    ...
    END LOOP;
END sample;
于 2011-01-18T19:51:46.563 回答