【问题标题】:Calling a function that return a table with dates调用返回带有日期的表的函数
【发布时间】:2012-09-18 00:14:29
【问题描述】:

我的函数有问题。 我需要一张包含月份日期、日期名称和该月份工作日数量的表格。 我已经在这里找到了一些帮助并根据我的需要进行了调整,但是我无法运行/编译该函数 这是我目前所拥有的:

create or replace TYPE DATE_ROW AS OBJECT
(
  MyDate DATE,
  Dayname VARCHAR2(12),
  Amount Integer
 )

create or replace TYPE DATE_TABLE as table of DATE_ROW

create or replace FUNCTION myfinaldays (mydate date)
       RETURN DATE_TABLE
       PIPELINED
    IS
       V_MYDATE          DATE;
       V_DAYNAME   VARCHAR2(12);
       V_AMOUNT           NUMBER;
BEGIN
    with this_month as (
        select trunc(to_date('mydate'), 'mm')+level-1 dy
          from dual 
       connect by level < (trunc(add_months(to_date('mydate'),1), 'mm')- trunc(to_date('mydate'), 'mm'))+1
    )
    FOR i IN 0 .. (select count(*) from this_month) LOOP
        select (dy) Daydate,
               to_char(dy, 'day'), Dayname,
              ( select count(*)
                  from this_month 
                 where  to_char(dy, 'dy') not in ('sat', 'sun')
               ) Amount
          from this_month
         where to_char(dy, 'dy') not in ('sat', 'sun')
           and EXTRACT(day from to_date(dy)) = i;

       pipe row (date_row(v_mydate,v_dayname, v_amount));
   END LOOP;
   RETURN;
   END;

函数调用将类似于:

select * from date_table(cast(myfinaldays('01.02.12')));

我只能给出一个日期作为参数。

我希望有人可以在这里帮助我,因为这让我慢慢发疯。 任何想法、示例或想法将不胜感激。

更新:

好的,这里是 mybe 的更新,为我的问题提供了一些更有用的信息:

这是有效的,我的目标是把它放在一个函数中,这样我就可以用 1 个参数调用它:

    with this_month as (  
    select trunc(to_date('01.02.12'), 'mm')+level-1 dy
    from   dual 
    connect by level < (trunc(add_months(to_date('01.02.12'),1), 'mm')- trunc(to_date('01.02.12'), 'mm'))+1
    )
    select (dy) mydate, (select count(*) from this_month) Days_in_month
    ,      to_char(dy, 'day') Dayname
    ,     ( select count(*) from this_month where  to_char(dy, 'dy') not in ('sat', 'sun') ) Amount
    from   this_month
    where  to_char(dy, 'dy') not in ('sat', 'sun') ;

对于我在最后添加的循环:'and EXTRACT(day from to_date(dy))=i'。

我添加了一个日期,这样你就可以看到我最后需要什么。如果我改写 mydate 并在开发人员中运行它后输入 01.02.12 作为参数,它仍然可以工作。

编译时出现的错误: - 错误(10,1): PL/SQL: SQL 语句被忽略

- Error(15,5): PL/SQL: ORA-00928: Keyword SELECT missing

- Error(22,8): PLS-00113: END-Definer 'LOOP' must complete 'myfinaldays' in row 1, comlumn 10 

- Error(23,4): PLS-00103: Found the symbol "RETURN" 

由于我的Oracle没有运行英文,所以错误被翻译,所以我希望猜对了。

【问题讨论】:

  • 您是在编译或调用函数时遇到错误;无论哪种方式,你会得到什么错误?我不认为您可以使用with 子句作为for 循环的“输入”,因为您正在混合SQL 和PL/SQL。您可能需要构建一个 PL/SQL 表来保存生成的日期。但对于初学者来说,to_date('mydate')mydate 已经是一个日期,而'mydate' 是一个与之无关的字符串...

标签: sql oracle function date


【解决方案1】:

这里有很多问题。

  • 您不能在 for i in 1..x 中直接使用 select(它应该是 1,而不是 0),您必须选择一个局部变量并将其用作最终值。
  • 您不能使用with 作为for 循环的输入;但你可以在那里使用光标,如下所示。
  • i 表示星期六或星期日(在本例中为第四个,假设您在二月运行它)时,循环内的选择 get no-data-found。
  • 获取每一行的所有非周末行的计数是低效的,尽管对于如此少量的数据并不重要。
  • 你指的是to_date('mydate'); mydate 已经是一个日期,而 'mydate' 是一个与之无关的字符串。

也许还有一些其他的东西,比如假设日期格式和语言环境,因为to_char(x, 'dy') 不会在任何地方返回satsun,但我会假设这将以受限的方式使用,所以你不会太在意这些。

通过尝试找出您期望的输出,我认为这将起作用(修改类型以获得您添加到原始问题的days_in_month):

create or replace type date_row as object
(
    mydate DATE,
    dayname VARCHAR2(12),
    days_in_month NUMBER,
    amount NUMBER
)
/

create or replace type date_table as table of date_row
/

create or replace function myfinaldays (mydate date)
return date_table pipelined deterministic is
begin
    for r in (
        select *
        from (
            select dy as daydate,
                to_char(dy, 'day') as dayname,
                count(*) over () as days_in_month,
                sum(case when to_char(dy, 'dy') in ('sat', 'sun')
                    then 0 else 1 end) over () as amount
            from (
                select trunc(mydate, 'mm')+level-1 dy
                from dual 
                connect by level < (trunc(add_months(mydate, 1), 'mm')
                    - trunc(mydate, 'mm')) + 1
            )
        )
        where to_char(daydate, 'dy') not in ('sat', 'sun')
    ) loop
       pipe row (date_row(r.daydate, r.dayname, r.days_in_month, r.amount));
    end loop;
end myfinaldays;
/

(如果你不想要days_in_month,那么你可以移动sat/sun检查并使用count代替amount而不是sum(case)。外部select是当前在一周中的某一天进行过滤,因此您可以计算所有天数,但如果您不想要该值,这不是问题。

然后调用为:

alter session set nls_date_format = 'DD.MM.RR';
select * from table(myfinaldays(to_date('01.02.12', 'DD.MM.RR')));

MYDATE   DAYNAME      DAYS_IN_MONTH     AMOUNT
-------- ------------ ------------- ----------
01.02.12 wednesday               29         21
02.02.12 thursday                29         21
03.02.12 friday                  29         21
06.02.12 monday                  29         21
...
29.02.12 wednesday               29         21

21 rows selected.

【讨论】:

  • 哇,非常感谢。是的,它做到了。我想我将特别看一下该代码,以便了解它在每个步骤中所做的事情。不过还有一件事:现在我应该可以像使用其他任何方法一样使用这张桌子了吗?比如'从表中选择 mydate(myfinaldays(to_date('01.02.12', 'DD.MM.RR'))) where mydate not in (select date from holidaycalendar)' 或者用左连接,联合等等....我必须测试什么效果最好。只是猜测那一步的语法,但我相信你明白我的意思。
  • 好的,它可以满足我的所有需求...再次感谢您的帮助。
  • @Thevagabond - 没问题。您可以将其视为一个表,但我会有点担心将另一个表中的字段作为mydate 参数传递。对于排除假日日期,您可能希望在函数内执行此操作,因此您不必在其他地方重复该操作。如果您还没有,请参阅 [常见问题解答] 了解如何确认有用的答案。
【解决方案2】:

由于您的函数是表值函数(返回表),因此您不能将其称为常规函数。 你已经加入了:

Oracle 中的类似内容:

select * from date_table t
cross join table( myfinaldays(<t.date colum>)) 

【讨论】:

  • date_table 是一个类型;这不应该只是select * from table(myfinaldays(to_date('01.02.12','DD.MM.RR'))吗? (当然是猜测日期格式,但更愿意看到显式转换)。
  • @Alex Poole 第一个问题是函数本身无法编译......而且我无法修复它。但是感谢您的建议,我想这将是我遇到的下一个问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-01
  • 2020-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多