With 语句 select 和函数进入游标

With statement select and function into cursor

可以在游标内使用带有函数的 with 结构,我不知道我是否声明不当,我收到以下错误 using with function inside procedure pl sql statement is not supported

CURSOR c_detail IS
WITH 
FUNCTION CALC_NUMBER(FOB_ITEM    NUMBER DEFAULT 0, 
                     FOB_TOTAL   NUMBER DEFAULT 0, 
                     WEIGHT NUMBER DEFAULT 0) RETURN NUMBER
     IS
     PESO_BRUTO_ITEM NUMBER :=0;
     BEGIN
         IF( (FOB_ITEM > 0) AND (FOB_TOTAL > 0 AND WEIGHT> 0 )) THEN
             PESO_BRUTO_ITEM := (FOB_ITEM * WEIGHT) / FOB_TOTAL;
         END IF;
         RETURN PESO_BRUTO_ITEM;
END CALC_NUMBER;  
test_data AS
(
SELECT 36.25 AS FOB_I, 12536.36 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
SELECT 15.36 AS FOB_I, 3678.65 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
SELECT 878.77 AS FOB_I, 89653.13 AS FOB_TOTAL, 362 AS W FROM dual
)
SELECT TD.FOB_I,
       TD.FOB_TOTAL,
       CALC_NUMBER(TD.FOB_I, TD.FOB_TOTAL, TD.W) WEIGHT
  FROM test_data TD

错误消息清楚地表明你不能做你想做的事,但如果你的光标看起来像下面这样,你就不必这样做了

CURSOR c_detail IS
WITH test_data AS(
    SELECT 36.25 AS FOB_I, 12536.36 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
    SELECT 15.36 AS FOB_I, 3678.65 AS FOB_TOTAL, 362 AS W FROM dual UNION ALL
    SELECT 878.77 AS FOB_I, 89653.13 AS FOB_TOTAL, 362 AS W FROM dual
)
SELECT TD.FOB_I,
       TD.FOB_TOTAL,
       CASE WHEN TD.FOB_I > 0 AND TD.FOB_TOTAL > 0 AND TD.W > 0 
       THEN (TD.FOB_I * TD.W) / TD.FOB_TOTAL
       ELSE 0
       END WEIGHT
FROM test_data TD
;

[TL;DR] 您可以在子查询分解子句中声明一个函数,但是(如 Justin Cave )它仅在您以动态方式执行查询时有效 SQL 并且在未来的数据库版本中可能会支持在游标内使用静态 SQL 中的函数。


这个带有函数的子查询分解子句在游标之外工作:

WITH
  FUNCTION with_function(
    p_id IN NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    RETURN 42 + p_id;
  END;
test_data ( id ) AS (
  SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3 
)
SELECT id,
       with_function( id )
FROM   test_data;

尝试将其放入游标(在 Oracle 18c 中):

DECLARE
  p_id NUMBER;
  p_fn NUMBER;

  CURSOR c_detail IS
  WITH
    FUNCTION with_function(
      p_id IN NUMBER
    ) RETURN NUMBER
    IS
    BEGIN
      RETURN 42 + p_id;
    END;
  test_data ( id ) AS (
    SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3 
  )
  SELECT id,
         with_function( id )
  FROM   test_data;
BEGIN
  OPEN c_detail;
  LOOP
    FETCH c_detail INTO p_id, p_fn;
    EXIT WHEN c_detail%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE( p_id || ', ' || p_fn );
  END LOOP;

  CLOSE c_detail;
END;
/

输出错误:

ORA-06550: line 7, column 14:
PL/SQL: ORA-00905: missing keyword
ORA-06550: line 6, column 3:
PL/SQL: SQL Statement ignored
ORA-06550: line 13, column 5:
PLS-00103: Encountered the symbol "END" when expecting one of the following:

   begin function pragma procedure subtype type <an identifier>
   <a double-quoted delimited-identifier> current cursor delete
   exists prior

删除函数,然后光标工作:

DECLARE
  p_id NUMBER;

  CURSOR c_detail IS
  WITH
  test_data ( id ) AS (
    SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3 
  )
  SELECT id
  FROM   test_data;
BEGIN
  OPEN c_detail;
  LOOP
    FETCH c_detail INTO p_id;
    EXIT WHEN c_detail%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE( p_id );
  END LOOP;

  CLOSE c_detail;
END;
/

因此在游标中使用子查询分解子句不是问题。

正在以动态方式执行游标SQL 查询:

DECLARE
  p_id NUMBER;
  p_fn NUMBER;
  c_detail SYS_REFCURSOR;

  p_sql VARCHAR2(4000) := 'WITH
    FUNCTION with_function(
      p_id IN NUMBER
    ) RETURN NUMBER
    IS
    BEGIN
      RETURN 42 + p_id;
    END;
  test_data ( id ) AS (
    SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 3 
  )
  SELECT id,
         with_function( id )
  FROM   test_data';
BEGIN
  OPEN c_detail FOR p_sql;
  LOOP
    FETCH c_detail INTO p_id, p_fn;
    EXIT WHEN c_detail%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE( p_id || ', ' || p_fn );
  END LOOP;

  CLOSE c_detail;
END;
/

作品和产出:

1, 43
2, 44
3, 45

看来,是的,您可以在子查询分解子句中声明一个函数,但它仅在您以动态方式执行查询时才有效 SQL。

db<>fiddle here