为什么查询中的游标不适用于 "with" 语句?

Why the cursor inside a query doesn't work with the "with" statement?

我曾尝试在 sql 查询中使用游标。这有效:

SELECT 1, CURSOR (SELECT 1 FROM DUAL) 
    FROM DUAL;

但是当我试图在 with 语句中使用这个子查询时。它不再起作用了。

WITH t (a, b) AS (
    SELECT 1, CURSOR (SELECT 1 FROM DUAL) 
    FROM DUAL)
SELECT t.a
  FROM t;

[Error] Execution (5: 10): ORA-00900: invalid SQL statement

为什么?

来自CURSOR expression documentation:

Restrictions on CURSOR Expressions

The following restrictions apply to CURSOR expressions:

  • If the enclosing statement is not a SELECT statement, then nested cursors can appear only as REF CURSOR arguments of a procedure.
  • If the enclosing statement is a SELECT statement, then nested cursors can also appear in the outermost select list of the query specification or in the outermost select list of another nested cursor.
  • Nested cursors cannot appear in views.
  • You cannot perform BIND and EXECUTE operations on nested cursors.

不幸的是,措辞不清楚,因为第一个要点中的“只能出现”在第二个要点中得到了扩展,措辞可能更清晰:

  • If the enclosing statement is a SELECT statement, then nested cursors can also appear ONLY in the outermost select list of the query specification or in the outermost select list of another nested cursor.

如果需要,您可以将 CURSOR 表达式包装在 PL/SQL 存储函数中,以将其转换为集合数据类型(或执行一些其他操作和 return 标量) .

例如定义函数:

CREATE FUNCTION pipe(
  i_cur IN SYS_REFCURSOR
) RETURN SYS.ODCINUMBERLIST PIPELINED
IS
  v_value PLS_INTEGER;
BEGIN
  LOOP
    FETCH i_cur INTO v_value;
    EXIT WHEN i_cur%NOTFOUND;
    PIPE ROW (v_value);
  END LOOP;
  
  CLOSE i_cur;
END;
/

然后:

WITH t (a, b) AS (
  SELECT 1,
         pipe(CURSOR(SELECT 1 FROM DUAL))
  FROM   DUAL
)
SELECT t.a
FROM   t;

有效,但删除函数调用会出现错误。

您还可以使用:

WITH FUNCTION sum_cursor(i_cur IN SYS_REFCURSOR) RETURN NUMBER
IS
  v_total NUMBER := 0;
  v_value NUMBER;
BEGIN
  LOOP
    FETCH i_cur INTO v_value;
    EXIT WHEN i_cur%NOTFOUND;
    IF v_value IS NOT NULL THEN
      v_total := v_total + v_value;
    END IF;
  END LOOP;
  
  CLOSE i_cur;
  
  RETURN v_total;
END;
t (a, b) AS (
  SELECT 1,
         sum_cursor(CURSOR(SELECT 2 FROM DUAL))
  FROM   DUAL
)
SELECT t.*
FROM   t;

输出:

A B
1 2

db<>fiddle here