我有一个简单的场景,我们尝试将一行插入表中并将创建的 ID 返回给客户端。我认为这将是一个简单的解决方案(在 11g 中创建的表,因此没有标识列,将其更改为标识列不是一个选项):
DECLARE
new_ID INT;
BEGIN
SELECT MAX(MY_TABLE_ID) + 1
INTO new_ID
FROM MY_SCHEMA.MY_TABLE
;
INSERT INTO MY_SCHEMA.MY_TABLE (MY_TABLE_ID)
VALUES (new_ID)
;
SELECT new_ID
FROM dual
;
COMMIT;
END;
但这会返回错误“PLS-00428:此 SELECT 语句中需要 INTO 子句”。事实证明,Oracle 不允许像 SQL Server 那样将变量用作 SELECT 子句中的表达式。
那么我们如何将这些数据返回给调用客户端呢? Every single answer关于我所看到的主题建议使用DBMS_OUTPUT来返回这个(就像这些问题中的提问者一样,我从SQL Server背景中来到这里感到困惑)。但 DMBS_OUTPUT 是一个调试工具,不适合生产代码(并非每个客户端都是 IDE!)。好的,我使用 ODP.NET 作为客户端,虽然 可以检索 PUT_LINE() 输出,但我确信开发人员会理解为什么这是一个需要避免的坏主意。同样,我不太习惯使用 RETURN 语句,在我看来,该语句应该只返回非 0 值作为错误状态。
唯一合理的方法是使用 SQL Server 的 OUTPUT.INSERTED 的 Oracle 等效项(由于显而易见的原因,@@ROWID 的等效项不是一个选项),但事实证明 RETURNING 子句还需要... INTO 子句,意味着我们再次使用变量。
在这种情况下,最巧妙的解决方案似乎是(我假设事务足够隔离以避免选择不同的执行 ID):
INSERT INTO MY_SCHEMA.MY_TABLE (MY_TABLE_ID)
SELECT MAX(MY_TABLE_ID) + 1
FROM MY_SCHEMA.MY_TABLE
;
SELECT AS NewId
FROM MY_SCHEMA.MY_TABLE
;
COMMIT;
它必须做,但无论是在性能还是编码方面,必须两次击中桌子似乎很笨重。但最终我的问题是:有没有什么方法可以将变量的值返回给客户端,而不必先将其插入表中,或者使用不适合这种用途的方法?
一种选择是创建一个带有 OUT 参数的过程,该参数将该值返回给调用者。这是一个例子:
SQL> CREATE TABLE test
2 (
3 id NUMBER,
4 name VARCHAR2 (20)
5 );
Table created.
SQL> SET SERVEROUTPUT ON
SQL>
SQL> CREATE OR REPLACE PROCEDURE p_new_id (o_new_id OUT test.id%TYPE)
2 IS
3 BEGIN
4 SELECT NVL (MAX (id), 0) + 1 INTO o_new_id FROM test;
5
6 INSERT INTO test (id, name)
7 VALUES (o_new_id, 'Test');
8
9 COMMIT;
10 END;
11 /
Procedure created.
测试:因为这(仍然是)PL/SQL,所以我使用匿名块,该块接受该值到本地变量中并将其显示在屏幕上。你可能会做一些不同的事情。
SQL> DECLARE
2 l_new_id test.id%TYPE;
3 BEGIN
4 p_new_id (l_new_id);
5
6 DBMS_OUTPUT.put_line ('Newly inserted ID = ' || l_new_id);
7 END;
8 /
Newly inserted ID = 1
PL/SQL procedure successfully completed.
SQL> /
Newly inserted ID = 2
PL/SQL procedure successfully completed.
SQL> SELECT * FROM test;
ID NAME
---------- --------------------
1 Test
2 Test
SQL>
另一方面:请注意,这是显示您所要求的示例:如何将值返回给客户端。
如果您确实打算使用这种方法来查找(好吧,插入)下一个
ID
值,那么这很可能是错误的。它在单用户环境中工作正常,但在多用户环境中会产生重复项(或者引发错误,如果 ID 是唯一的),因为迟早两个(或更多)用户会尝试在同一位置执行相同的操作。同一时间。
也许您宁愿让数据库通过标识列(Oracle 12c 及以上)或较低数据库版本的序列+触发器来处理该问题。
此外,在 Oracle 中,我们通常让调用者决定何时提交(即我会将其从过程中删除)。