【问题标题】:Can't run DDL statement in an Oracle query无法在 Oracle 查询中运行 DDL 语句
【发布时间】:2018-10-31 18:03:40
【问题描述】:

我需要为查询提取不同格式的源名称列表。我编写了一个函数来执行此操作,它返回我想要的数据,但是,因为它截断并写入一个表,它不能在 Oracle 的查询中使用。所以我对如何在查询中进行这项工作感到困惑。这是代码。

FUNCTION GET_POD_SOURCE_FROM_APP_NO
     (I_APPL_ID_SEQ IN WRD_APPLICATIONS.APPL_ID_SEQ%TYPE,
      I_DELIMITER IN VARCHAR2 DEFAULT ';')

   RETURN VARCHAR2
   IS

   CURSOR C1 IS
    SELECT DISTINCT SOUR.SOUR_ID_SEQ,
           SRNM.SRNM_NM,
           SOUR.FORK_NM,
           DECODE(POD.UNNAMED_TRIBUTARY,
                  'N',
                  NULL,
                  'Y',
                  'UNNAMED TRIBUTARY') POD_UT,
           MRTP.DESCR POD_MINORTYPE,
           DECODE(POD.MAJOR_TYPE,
                  'S',
                  'SURFACE WATER',
                  'G',
                  'GROUNDWATER',
                  NULL,
                  NULL) POD_MAJORTYPE
      FROM WRD_SOURCES             SOUR,
           WRD_SOURCE_NAMES        SRNM,
           WRD_POINT_OF_DIVERSIONS POD,
           WRD_MINOR_TYPES         MRTP,
           WRD_VERSION_APPLICATION_XREFS VAX
     WHERE SOUR.SOUR_ID_SEQ = POD.SOUR_ID_SEQ
       AND SOUR.SRNM_ID_SEQ = SRNM.SRNM_ID_SEQ
       AND POD.MRTP_CD = MRTP.MRTP_CD(+)
       AND POD.WRGT_ID_SEQ = VAX.WRGT_ID_SEQ
       AND POD.VERS_ID_SEQ = VAX.VERS_ID_SEQ
       AND VAX.APPL_ID_SEQ = I_APPL_ID_SEQ;


   CURSOR C2 IS
    SELECT DISTINCT TS.SOURCE_FULL
    FROM WRD.TEMP_SOURCE TS
    ORDER BY TS.SOURCE_FULL;



  C1_R    C1%ROWTYPE;
  C2_R    C2%ROWTYPE;
  ROW_CNT PLS_INTEGER := 0;
  RTN_VAL VARCHAR2(4000);
  SRC_NAME VARCHAR2(400);

BEGIN

  EXECUTE IMMEDIATE 'TRUNCATE TABLE TEMP_SOURCE';

  FOR C1_R IN C1 LOOP

    IF C1_R.POD_UT IS NOT NULL THEN
      SRC_NAME := C1_R.POD_UT || ' OF ';
    END IF;

    SRC_NAME := SRC_NAME || C1_R.SRNM_NM;

    IF C1_R.FORK_NM IS NOT NULL THEN
      SRC_NAME := SRC_NAME ||', '|| C1_R.FORK_NM;
    END IF;

    IF C1_R.POD_MINORTYPE IS NOT NULL THEN
      SRC_NAME := C1_R.POD_MINORTYPE || ', ' || SRC_NAME;
    END IF;

    EXECUTE IMMEDIATE 'INSERT INTO WRD.TEMP_SOURCE VALUES (SRC_NAME)';
    EXECUTE IMMEDIATE 'COMMIT';

    SRC_NAME := '';
  END LOOP;


  FOR C2_R IN C2 LOOP
    ROW_CNT := ROW_CNT + 1;

    IF (ROW_CNT < 2) THEN
      RTN_VAL := C2_R.SOURCE_FULL;
    ELSE
      RTN_VAL := SUBSTR(RTN_VAL, 1, 3600) || I_DELIMITER || ' ' || C2_R.SOURCE_FULL;
    END IF;
  END LOOP;

  RETURN(TRIM(RTN_VAL));

END GET_POD_SOURCE_FROM_APP_NO;

谢谢。

【问题讨论】:

  • 不要尝试在查询中运行,在 PL/SQL 中调用它。- 有几种方法可以显示 RTN_VAL,具体取决于您将如何使用此过程。跨度>
  • 虽然实际的问题是您使用该 TEMP_SOURCE 表。只需编写一些简单的旧 SQL 即可完成此操作
  • 它在基于查询的 SSRS 报告中使用,并且应用程序编号来自该查询,因为它正在处理。我为大多数查询字段使用了一个视图,但该视图再次基于一个查询,一个非常大的查询,其中包含很多选择。

标签: oracle plsql ddl


【解决方案1】:

这是一个纯 SQL 版本,它不需要临时表和不必要的 DDL。

with C1_R as ( 
   SELECT DISTINCT SOUR.SOUR_ID_SEQ,
           SRNM.SRNM_NM,
           SOUR.FORK_NM,
           DECODE(POD.UNNAMED_TRIBUTARY,
                  'N',
                  NULL,
                  'Y',
                  'UNNAMED TRIBUTARY') POD_UT,
           MRTP.DESCR POD_MINORTYPE,
           DECODE(POD.MAJOR_TYPE,
                  'S',
                  'SURFACE WATER',
                  'G',
                  'GROUNDWATER',
                  NULL,
                  NULL) POD_MAJORTYPE
      FROM WRD_SOURCES             SOUR,
           WRD_SOURCE_NAMES        SRNM,
           WRD_POINT_OF_DIVERSIONS POD,
           WRD_MINOR_TYPES         MRTP,
           WRD_VERSION_APPLICATION_XREFS VAX
     WHERE SOUR.SOUR_ID_SEQ = POD.SOUR_ID_SEQ
       AND SOUR.SRNM_ID_SEQ = SRNM.SRNM_ID_SEQ
       AND POD.MRTP_CD = MRTP.MRTP_CD(+)
       AND POD.WRGT_ID_SEQ = VAX.WRGT_ID_SEQ
       AND POD.VERS_ID_SEQ = VAX.VERS_ID_SEQ
       AND VAX.APPL_ID_SEQ = I_APPL_ID_SEQ;
 ) , fmt as (
 select  distinct  nvl2(C1_R.POD_MINORTYPE, C1_R.POD_MINORTYPE || ', ', null)
            || nvl2(C1_R.POD_UT  C1_R.POD_UT || ' OF ', null)
            || C1_R.SRNM_NM
            || nvl2(C1_R.FORK_NM IS, ', '|| C1_R.FORK_NM, null)
           as SRC_NAME;
from C1_R
)
select listagg(src_name, '|') within group (order by source_name) as rtn_val
from fmt
/

这样做不好的一件事是处理超过 4000 字符限制的串联字符串。在 12cR2 中,Oracle 为 listagg() 子句提供了 ON OVERFLOW TRUNCATE,但几乎没有人使用 12cR2; Stew Ashton 有一个针对早期版本的解决方法。 Check it out.

【讨论】:

    【解决方案2】:

    如果 Wernfried 的解决方案不起作用,我只想指出,您可以像这样进行简单的格式化,而无需求助于临时表。我不想弄乱你的格式,所以我留下了你的第二个循环,但通常我也只使用 LISTAGG ,根本不使用 PLSQL。

    FUNCTION GET_POD_SOURCE_FROM_APP_NO
         (I_APPL_ID_SEQ IN WRD_APPLICATIONS.APPL_ID_SEQ%TYPE,
          I_DELIMITER IN VARCHAR2 DEFAULT ';')
    
       RETURN VARCHAR2
       IS
    
       CURSOR C1 IS
        SELECT 
            CASE WHEN POD_MINORTYPE is not null 
                THEN POD_MINORTYPE || ', ' 
                ELSE NULL END 
            || CASE WHEN POD_UT is not null 
                THEN POD_UT || ' OF ' 
                ELSE NULL END 
            || SRNM_NM
            || CASE WHEN FORK_NM is not null 
                THEN ', '|| FORK_NM
                ELSE NULL END 
            AS SOURCE_FULL
        FROM (
        SELECT DISTINCT SOUR.SOUR_ID_SEQ,
                   SRNM.SRNM_NM,
                   SOUR.FORK_NM,
                   DECODE(POD.UNNAMED_TRIBUTARY,
                          'N',
                          NULL,
                          'Y',
                          'UNNAMED TRIBUTARY') POD_UT,
                   MRTP.DESCR POD_MINORTYPE,
                   DECODE(POD.MAJOR_TYPE,
                          'S',
                          'SURFACE WATER',
                          'G',
                          'GROUNDWATER',
                          NULL,
                          NULL) POD_MAJORTYPE
              FROM WRD_SOURCES             SOUR,
                   WRD_SOURCE_NAMES        SRNM,
                   WRD_POINT_OF_DIVERSIONS POD,
                   WRD_MINOR_TYPES         MRTP,
                   WRD_VERSION_APPLICATION_XREFS VAX
             WHERE SOUR.SOUR_ID_SEQ = POD.SOUR_ID_SEQ
               AND SOUR.SRNM_ID_SEQ = SRNM.SRNM_ID_SEQ
               AND POD.MRTP_CD = MRTP.MRTP_CD(+)
               AND POD.WRGT_ID_SEQ = VAX.WRGT_ID_SEQ
               AND POD.VERS_ID_SEQ = VAX.VERS_ID_SEQ
               AND VAX.APPL_ID_SEQ = I_APPL_ID_SEQ
        ) TS;
    
      C1_R    C1%ROWTYPE;
      ROW_CNT PLS_INTEGER := 0;
      RTN_VAL VARCHAR2(4000);
      SRC_NAME VARCHAR2(400);
    
    BEGIN
    
      FOR C1_R IN C1 LOOP
        ROW_CNT := ROW_CNT + 1;
    
        IF (ROW_CNT < 2) THEN
          RTN_VAL := C1_R.SOURCE_FULL;
        ELSE
          RTN_VAL := SUBSTR(RTN_VAL, 1, 3600) || I_DELIMITER || ' ' || C1_R.SOURCE_FULL;
        END IF;
      END LOOP;
    
      RETURN(TRIM(RTN_VAL));
    
    END GET_POD_SOURCE_FROM_APP_NO;
    /
    

    【讨论】:

    • 这太棒了。有时我们太投入了,以至于看不到东西。我现在就试试这个。
    • 你被录用了.....非常令人印象深刻。一旦我添加了订单,它就可以完美运行。
    【解决方案3】:

    这两个命令不需要是动态的:

    EXECUTE IMMEDIATE 'INSERT INTO WRD.TEMP_SOURCE VALUES (SRC_NAME)';
    EXECUTE IMMEDIATE 'COMMIT';
    

    只需运行INSERT INTO WRD.TEMP_SOURCE VALUES (SRC_NAME);

    我认为主要问题是您的TRUNCATE ...COMMIT。这两个命令都结束了一个事务,这在查询中是不允许的。我假设表TEMP_SOURCEGLOBAL TEMPORARY 表。

    将您的TRUNCATE 更改为DELETE FROM WRD.TEMP_SOURCE; 并删除COMMIT。我不确定,但它应该可以工作(我没有测试过)。

    【讨论】:

    • 你不能在从查询调用的函数中做任何 DML,所以静态删除和插入仍然是个问题,不是吗?
    • 正如我所说,很可能我弄错了。我习惯经常使用 RefCursors,它工作正常。
    猜你喜欢
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 2018-05-14
    • 2014-10-16
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 2021-12-28
    相关资源
    最近更新 更多