如何从 Oracle PL/SQL 函数中返回现有表中的记录?

Posted

技术标签:

【中文标题】如何从 Oracle PL/SQL 函数中返回现有表中的记录?【英文标题】:How to return a record from an existing table from an Oracle PL/SQL function? 【发布时间】:2010-02-25 18:55:30 【问题描述】:

我知道这似乎是一件基本的事情,但我以前从未这样做过。

我想从现有表中返回一条记录作为 Oracle PL/SQL 函数的结果。我已经找到了几种不同的方法来做到这一点,但我对 best 方法很感兴趣(阅读:我对我的发现并不那么满意)。

我正在做的事情是……我有一个名为“users”的表,我想要一个函数“update_and_get_user”,它给出了一个用户名(以及关于该用户的其他可信信息)可能会执行'users' 表上的各种操作,然后从所述表中返回零或一行/记录。

这是目前我脑海中代码的基本轮廓(阅读:不知道语法是否接近正确):

CREATE FUNCTION update_and_get_user(UserName in VARCHAR2, OtherStuff in VARCHAR2)
    RETURN users PIPELINED IS
  TYPE ref0 IS REF CURSOR;
  cur0       ref0;
  output_rec users%ROWTYPE;
BEGIN
  -- Do stuff

  -- Return the row (or nothing)
  OPEN cur0 FOR 'SELECT * FROM users WHERE username = :1'
    USING UserName;

  LOOP
    FETCH cur0 INTO output_rec;
    EXIT WHEN cur0%NOTFOUND;
    PIPE ROW(output_rec);
  END LOOP;
END update_and_get_user;

我已经看到返回记录或表的示例,记录或表的类型已预先创建/声明,但似乎如果表已经定义,我应该能够利用它,并且因此,如果曾经进行过表更改,则不必担心同步类型声明代码。

我对所有潜在的解决方案和评论持开放态度,但我确实真的希望将其保留在单个 PL/SQL 函数中(而不是使用与数据库多次,以某种形式的 'SELECT * FROM users WHERE username=blah')作为系统调用函数和数据库本身可能是不同的城市。在这个限制之外,我愿意改变我的想法。

【问题讨论】:

【参考方案1】:

这就是我的做法。 Oracle 中的变量/表名/列名不区分大小写,所以我会使用user_name 而不是UserName

CREATE TABLE users( UserName varchar2(20), OtherStuff VARCHAR2(20) );

函数update_and_get_user。请注意,我返回的是 ROWTYPE,而不是流水线表。

CREATE OR REPLACE FUNCTION update_and_get_user(
  in_UserName   IN users.UserName%TYPE,
  in_OtherStuff IN users.OtherStuff%TYPE )
RETURN users%ROWTYPE
IS
  output_rec users%ROWTYPE;
BEGIN
  UPDATE users
  SET OtherStuff = in_OtherStuff
  WHERE UserName = in_UserName
    RETURNING UserName, OtherStuff
    INTO output_rec;
  RETURN output_rec;
END update_and_get_user;

这就是你的称呼。您不能将ROWTYPE 设为NULL,但您可以将username 设为例如。

DECLARE
  users_rec users%ROWTYPE;
BEGIN
  users_rec := update_and_get_user('user', 'stuff');
  IF( users_rec.username IS NOT NULL ) THEN
    dbms_output.put_line('FOUND: ' || users_rec.otherstuff);
  END IF;
END;

下面是使用PIPED ROWS 的解决方案,但它不起作用。您不能在查询中更新表。

SELECT * FROM TABLE(update_and_get_user('user', 'stuff'))

ORA-14551: cannot perform a DML operation inside a query

解决方案如下所示:

CREATE OR REPLACE TYPE users_type
AS OBJECT
(
  username   VARCHAR2(20),
  otherstuff VARCHAR2(20)
)

CREATE OR REPLACE TYPE users_tab
   AS TABLE OF users_type;

CREATE OR REPLACE FUNCTION update_and_get_user(
  in_UserName   IN users.username%TYPE,
  in_OtherStuff IN users.otherstuff%TYPE )
RETURN users_tab PIPELINED
IS
  output_rec users%ROWTYPE;
BEGIN
  UPDATE users
  SET OtherStuff = in_OtherStuff
  WHERE UserName = in_UserName
    RETURNING username, otherstuff
    INTO output_rec;
  PIPE ROW(users_type(output_rec.username, output_rec.otherstuff));
END;

【讨论】:

+1 综合回答彼得 - 你打败了我(需要加快我的打字速度)如果需要保罗,这个链接(download.oracle.com/docs/cd/E11882_01/appdev.112/e10472/…)解释了 %rowtype 属性 +1 彼得!我希望我能投多票。首先,这看起来真的很好。其次,我不知道您可以在更新中执行“返回...进入...”,这真的很酷并且会提高性能。第三,我不知道不能在表函数的查询中执行更新,这最终会让我非常沮丧。一旦我实现/验证它,我会将其标记为已接受的答案。谢谢! @Paul:我不认为你可以在函数内部调用 DML -- 如果 SELECT 语句调用它会发生什么? @Adam Musch:Oracle 允许在函数中使用 DML。从 PL/SQL 调用时它工作正常,但如果在 SELECT 语句中调用它,您仍然会得到 ORA-14551。如果对此不满意,请考虑编写一个过程而不是函数并将ROWTYPE 作为OUT-参数返回。 @Adam Musch:在我的特殊情况下,此函数最常从 .NET 应用程序调用,而不是 SELECT 语句(它是自定义 MembershipProvider 的一部分)。在其他极少数情况下,它只需要来自 PL/SQL 函数,正如 Peter 所描述的那样,这很好。

以上是关于如何从 Oracle PL/SQL 函数中返回现有表中的记录?的主要内容,如果未能解决你的问题,请参考以下文章

带有参数的 PL/SQL 过程/函数从选择查询返回表

从 pl/sql 中的函数返回数据作为光标而不创建类型 oracle 11g

如何在Oracle中仅将列用作1行到1行返回pl / sql函数中的参数

由于 Oracle DB 从 12c 升级到 19c,对现有 SQL、PL/SQL 脚本和数据模型有何影响?

从基于 PL/SQL 函数体返回 SQL 查询的 Oracle APEX 5 经典报告下载到 Excel 选项,

如何从 Oracle 10G PL/SQL 函数和过程中查找所有表引用? [复制]