是否可以在 PL SQL 程序中创建本地视图?

Is it possible to create a local view in PL SQL procedure?

我正在寻找一个解决方案,告诉 Oracle“这个查询应该像视图一样被重用”(如果我问,那是我不想要的to/can如果可能,不要创建完整的全局视图。

使用以下最小模型(我省略了其他列):

因此下面的创建语句(我没有针对Oracle验证它们,更多的是为了理解问题 比测试)。

create table owners(  owner_id  number not null, constraint pk_owners primary key (owner_id));
create table books(   book_id   number not null, constraint pk_books primary key (book_id));
create table authors( author_id number not null, constraint pk_authors primary key (author_id));
create table chapters( chapter_id number not null, constraint pk_chapters primary key (chapter_id));

create table owned_books(
  owner_id number not null,
  book_id  number not null, 
  constraint pk_owned_books primary key (owner_id, book_id),
  constraint fk_owned_books_1 foreign key (owner_id) references owners (owner_id),
  constraint fk_owned_books_2 foreign key (book_id) references books (book_id) 
);
create table book_authors(
  book_id number not null,
  author_id  number not null, 
  constraint pk_book_authors primary key (book_id, author_id),
  constraint fk_book_authors_1 foreign key (author_id) references authors (author_id),
  constraint fk_book_authors_2 foreign key (book_id) references books (book_id) 
);
create table chapter_authors(
  chapter_id number not null,
  author_id  number not null, 
  constraint pk_chapter_authors primary key (chapter_id, author_id),
  constraint fk_chapter_authors_1 foreign key (author_id) references authors (author_id),
  constraint fk_chapter_authors_2 foreign key (chapter_id) references chapters (chapter_id) 
);
create table book_chapters(
  chapter_id number not null,
  book_id  number not null, 
  constraint pk_book_chapters primary key (chapter_id, book_id),
  constraint fk_book_chapters_1  foreign key (chapter_id) references chapters (chapter_id)
  constraint fk_book_chapters_2 foreign key (book_id) references books (book_id)  
);

create table authors_related_data1( 
  author_id number not null,
  constraint pk_authors_related_data1 primary key (author_id),
  constraint fk_authors_related_data1_1 foreign key (author_id) references authors (author_id)
);
create table authors_related_data2(
  data_id number not null,
  author_id number not null,
  constraint pk_authors_related_data2 primary key (data_id),
  constraint authors_related_data2 foreign key (author_id) references authors (author_id)
);

我想做的查询(和重复的部分):

with v_books as (
  select books.book_id
  from owned_books 
  inner join books on books.book_id = owned_books.book_id
  where owned_books.owner_id = P_OWNER_ID
), v_authors as (
    select authors.author_id
    from v_books
    inner join book_authors on book_authors.book_id = v_books.book_id
    inner join authors on authors.author_id = book_authors.author_id
  union
    select authors.author_id
    from v_books
    inner join book_chapters on book_chapters.book_id = v_books.book_id
    inner join chapter_authors on chapter_authors.chapter_id = book_chapters.chapter_id
    inner join authors on authors.author_id = book_chapters.author_id  
)
  select authors_related_data1.*
  from   v_authors
  inner join authors_related_data1 on authors_related_data1.author_id = v_authors.author_id
;

with v_books as (
  select books.book_id
  from owned_books 
  inner join books on books.book_id = owned_books.book_id
  where owned_books.owner_id = P_OWNER_ID
), v_authors as (
    select authors.author_id
    from v_books
    inner join book_authors on book_authors.book_id = v_books.book_id
    inner join authors on authors.author_id = book_authors.author_id
  union
    select authors.author_id
    from v_books
    inner join book_chapters on book_chapters.book_id = v_books.book_id
    inner join chapter_authors on chapter_authors.chapter_id = book_chapters.chapter_id
    inner join authors on authors.author_id = book_chapters.author_id  
)
  select authors_related_data2.*
  from   v_authors
  inner join authors_related_data2 on authors_related_data2.author_id = v_authors.author_id
;

两个查询的第一部分(带有...)相同。

这样的景色会很棒:

create view v_owned_authors as (
  with v_books as (
    select books.book_id
    from owned_books 
    inner join books on books.book_id = owned_books.book_id
  )
    (
        select authors.author_id
        from v_books
        inner join book_authors on book_authors.book_id = v_books.book_id
        inner join authors on authors.author_id = book_authors.author_id
      union
        select authors.author_id
        from v_books
        inner join book_chapters on book_chapters.book_id = v_books.book_id
        inner join chapter_authors on chapter_authors.chapter_id = book_chapters.chapter_id
        inner join authors on authors.author_id = book_chapters.author_id  
    )
;

之前的查询会很简单:

  select authors_related_data2.*
  from   v_owned_authors
  inner join authors_related_data2 on authors_related_data2.author_id = v_authors.author_id
  where v_owned_authors.owner_id = P_OWNER_ID

但是:

这样的流程能满足您的需求吗?

  create or replace procedure p_statement (cur in out sys_refcursor)
  is   
  begin
     open cur for 
     select 1 num from dual union select 2 num from dual;
  end;

(我把 select 从 dual 放在那里,相反你可以用你的复杂查询替换它并相应地定义要提取的变量)

实际调用可能是这样的:

   declare
    mycur sys_refcursor;
    num number;
  begin
      p_statement(mycur);
   LOOP
   FETCH mycur INTO num;
    EXIT WHEN mycur%NOTFOUND;
    DBMS_OUTPUT.put_line(num);
   END LOOP;
  end;

您可以使用 table 函数或流水线函数

这是一个 table 函数的示例,来自: http://oracle-base.com/articles/misc/pipelined-table-functions.php

CREATE TYPE t_tf_row AS OBJECT (
  id           NUMBER,
  description  VARCHAR2(50)
);
/

CREATE TYPE t_tf_tab IS TABLE OF t_tf_row;
/

-- Build the table function itself.
CREATE OR REPLACE FUNCTION get_tab_tf (p_rows IN NUMBER) RETURN t_tf_tab AS
  l_tab  t_tf_tab := t_tf_tab();
BEGIN
  FOR i IN 1 .. p_rows LOOP
    l_tab.extend;
    l_tab(l_tab.last) := t_tf_row(i, 'Description for ' || i);
  END LOOP;

  RETURN l_tab;
END;
/

-- Test it.
SELECT *
FROM   TABLE(get_tab_tf(10))
ORDER BY id DESC;

这是流水线函数的示例:

CREATE OR REPLACE FUNCTION get_tab_ptf (p_rows IN NUMBER) RETURN t_tf_tab PIPELINED AS
   r t_tf_row%rowtype;
BEGIN
  for z in (select id, desc from sometalbe) loop
    r.id := z.id;
    r.description := z.desc;
    PIPE ROW(r);   
  END LOOP;
 RETURN;
END;