Oracle: проблема с несогласованным типом данных

#oracle #plsql #ora-00932 #ora-06512

#Oracle #plsql #ora-00932 #ora-06512

Вопрос:

Я получаю сообщение об ошибке непоследовательного типа данных, и я не уверен, почему. Мне нужны некоторые рекомендации, чтобы разобраться в этом.

Я создаю два типа как:

В моей таблице юниверса есть следующие столбцы с типом столбца:


 Column Name                          Data Type

PON                                  VARCHAR2(25 BYTE)
RPON                             VARCHAR2(25 BYTE)
SUPPLIER_NAME                    VARCHAR2(255 BYTE)
SUB_SUPPLIER_NAME                    VARCHAR2(255 BYTE)
SOURCE_NO                            VARCHAR2(40 BYTE)
CKR                                  VARCHAR2(200 BYTE)
LEC_ID                           VARCHAR2(200 BYTE)
ICSC                             VARCHAR2(10 BYTE)
ACTL_ST                          VARCHAR2(10 BYTE)
ADW_ST                           VARCHAR2(10 BYTE)
PROJ_ID                          VARCHAR2(100 BYTE)
MOVE_TO_INV_DT                        DATE
IE_DT                                 DATE
DDD_DT                                DATE
EFF_BILL_DT                               DATE
ACTION                           VARCHAR2(10 BYTE)
SERVICE                          VARCHAR2(10 BYTE)
AFP                                  VARCHAR2(10 BYTE)
ACNA                             VARCHAR2(10 BYTE)
SERVICE_NAME                     VARCHAR2(255 BYTE)
UPLOAD_DT                                 DATE
PROGRAM                          VARCHAR2(50 BYTE)
INITIATIVE_ID                        NUMBER
ACOST                                NUMBER
ACOST_IND                            VARCHAR2(25 BYTE)
MAPFILE                          VARCHAR2(100 BYTE)
  

Тип строки

    create or replace
TYPE test_COMP_REPORT_ROW_TYPE AS OBJECT (
  PON   VARCHAR2(25 BYTE),
  RPON  VARCHAR2(25 BYTE),
  VENDOR VARCHAR2(255 BYTE),
  SUB_SUPPLIER VARCHAR2(255 BYTE),
  SOURCE_NO VARCHAR2(40 BYTE),
  ATT_CKT_ID VARCHAR2(200 BYTE),
  LEC_ID VARCHAR2(200 BYTE),
  ICSC VARCHAR2(10 BYTE),
  STATE VARCHAR2(10 BYTE),
  PROJECT_ID VARCHAR2(100 BYTE),
  ACTION VARCHAR2(10 BYTE),
  SERVICE_SPEED VARCHAR2(10 BYTE),
  SERVICE_NAME VARCHAR(255 BYTE),
  INEFFECT_DATE DATE,
  EVENT_DATE DATE, 
  DUE_DATE DATE, 
  ACOST NUMBER
  )
  

Тип вкладки

 create or replace type test_COMP_REPORT_TAB_TYPE 
           AS TABLE OF test_COMP_REPORT_ROW_TYPE
  

Вот функция, которая использует этот тип:

 create or replace
FUNCTION test_comp_report_func
  (
    start_dt_h IN VARCHAR2 DEFAULT NULL,
    end_dt_h   IN VARCHAR2 DEFAULT NULL,
    year_h     IN VARCHAR2 DEFAULT NULL )
  RETURN test_comp_report_tab_type pipelined
  IS
  e_sql LONG;
  program_v VARCHAR2(10);

v_row test_comp_report_row_type := test_comp_report_row_type(NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL,
                                                             NULL
                                                             );

  TYPE rectyp IS REF CURSOR;
    rrc_rectyp rectyp;
TYPE recordvar IS RECORD
( 
  PON   VARCHAR2(25 BYTE),
  RPON  VARCHAR2(25 BYTE),
  VENDOR VARCHAR2(255 BYTE),
  SUB_SUPPLIER VARCHAR2(255 BYTE),
  SOURCE_NO VARCHAR2(40 BYTE),
  ATT_CKT_ID VARCHAR2(200 BYTE),
  LEC_ID VARCHAR2(200 BYTE),
  ICSC VARCHAR2(10 BYTE),
  STATE VARCHAR2(10 BYTE),
  PROJECT_ID VARCHAR2(100 BYTE),
  ACTION VARCHAR2(10 BYTE),
  SERVICE_SPEED VARCHAR2(10 BYTE),
  SERVICE_NAME VARCHAR(255 BYTE),
  INEFFECT_DATE DATE,
  EVENT_DATE DATE, 
  DUE_DATE DATE, 
  ACOST NUMBER
);
  res_rec recordvar;

BEGIN  

    e_sql := e_sql || 'SELECT 
                      PON,
                      RPON,
                      SUPPLIER_NAME VENDOR, 
                      SUB_SUPPLIER_NAME SUB_SUPPLIER, 
                      SOURCE_NO,
                      CKR,
                      LEC_ID,
                      ICSC, 
                      ACTL_ST,
                      ADW_ST STATE, 
                      PROJ_ID,
                      ACTION,
                      SERVICE SPEED, 
                      AFP,
                      ACNA,
                      SERVICE_NAME,
                      IE_DT, 
                      MOVE_TO_INV_DT EVENTDAT,
                      DDD_DT DUEDATE, 
                      EFF_BILL_DT,
                      ACOST
                    FROM UNIVERSE
                    WHERE to_date(IE_DT) between to_date(nvl(''01/01/2000'', ''01/01/2000''), ''MM/DD/YYYY'') 
                      and to_date(nvl(''12/31/2009'', to_char(trunc(add_months(sysdate, 12),''year'')-1,''MM/DD/YYYY'')), ''MM/DD/YYYY'')
                    AND PROGRAM = ''T45sONNET''
                    AND nvl(trim(ACOST_IND), ''NULL'') not in (''INVALID-2005'')
                    ORDER BY ACTION';

dbms_output.put_line(e_sql);
OPEN rrc_rectyp FOR e_sql;
  LOOP
      FETCH rrc_rectyp INTO res_rec;
      EXIT WHEN rrc_rectyp%NOTFOUND;
      v_row.PON            := res_rec.PON;
      v_row.RPON           := res_rec.RPON;
      v_row.VENDOR         := res_rec.VENDOR;
      v_row.SUB_SUPPLIER   := res_rec.SUB_SUPPLIER;
      v_row.SOURCE_NO      := res_rec.SOURCE_NO;
      v_row.ATT_CKT_ID     := res_rec.ATT_CKT_ID;
      v_row.LEC_ID         := res_rec.LEC_ID;
      v_row.ICSC           := res_rec.ICSC;
      v_row.STATE          := res_rec.STATE;
      v_row.PROJECT_ID     := res_rec.PROJECT_ID;
      v_row.ACTION         := res_rec.ACTION;
      v_row.SERVICE_SPEED  := res_rec.SERVICE_SPEED;
      v_row.SERVICE_NAME   := res_rec.SERVICE_NAME;
      v_row.INEFFECT_DATE  := res_rec.INEFFECT_DATE;
      v_row.EVENT_DATE     := res_rec.EVENT_DATE;
      v_row.DUE_DATE       := res_rec.DUE_DATE;
      v_row.ACOST          := res_rec.ACOST;
      pipe ROW(v_row);
  END LOOP;
  return;

end test_comp_report_func;
  

Я пытался отладить проблему, но все еще не могу найти выход и был бы признателен, если сообщество SO сможет помочь.

Комментарии:

1. есть какие-либо подсказки или предложения относительно того, что может вызывать эти проблемы?

2. Что такое тип данных UNIVERSE.PON ?

3. У LONG есть куча проблем, вероятно, вам следует использовать что-то вроде varchar2 (32767) для E_SQL.

4. @jonearles — почему у long какие-то проблемы?

5. @Rachel: Длинные значения были заменены на LOBs, и они не обновлялись в течение десятилетия или двух. У них много ограничений, на этой странице есть несколько примеров: oracle-developer.net/display.php?id=430

Ответ №1:

Сначала я написал ответ, пытаясь воспроизвести вашу ошибку, но вы немного изменили свой вопрос, поэтому я начинаю снова с нуля.

Сначала несколько замечаний:

  • По вашему собственному признанию, вы новичок в PL / SQL, но используете довольно продвинутые функции: динамический SQL, конвейерные функции, объекты SQL. Давайте попробуем начать с чего-нибудь более простого для начала (я покажу вам, как вы можете работать со статическим SQL, этого было бы достаточно в 99,9% случаев).
  • Когда вы сталкиваетесь с проблемой, вам нужно разложить свой код, чтобы увидеть, что работает, а что нет. Обычно это означает упрощение вашего кода до тех пор, пока он не станет настолько простым, что начнет работать, затем возвращайте сложные элементы вашего кода один за другим, пока вы снова не столкнетесь с проблемой.
  • Когда вы предоставляете тестовый пример, постарайтесь сделать его как можно более простым 🙂 Людям будет проще помочь вам, но, что более важно, в большинстве случаев построение тестового примера поможет вам найти решение самостоятельно, поскольку это заставит вас разложить свой сложный код (см. Предыдущий пункт). Мое эмпирическое правило (FWIW) заключается в том, что код, который отображается с полосой прокрутки (горизонтальной или вертикальной) в SO, слишком велик для тестового примера и по возможности нуждается в обрезке.

Я запустил ваш код и получил ORA-00932 в fetch строке. Когда я заменяю SQL статическим SQL, ошибка становится более явной:

 SQL> CREATE OR REPLACE FUNCTION test_comp_report_func
  2     RETURN test_comp_report_tab_type
  3     PIPELINED IS
  4     TYPE recordvar IS RECORD(
  5        PON           VARCHAR2(25 BYTE),
           (...snip...)
 21        ACOST         NUMBER);
 22     res_rec recordvar;
 23     v_row   test_COMP_REPORT_ROW_TYPE;
 24     CURSOR rrc_rectyp IS
 25        SELECT PON,
                  (...snip...)
 45               ACOST
 46          FROM UNIVERSE;
 48  BEGIN
 49     OPEN rrc_rectyp;
 50     LOOP
 51        FETCH rrc_rectyp
 52           INTO res_rec;
 54        EXIT WHEN rrc_rectyp%NOTFOUND;
 55        /*...*/
 56        PIPE ROW(v_row);
 57     END LOOP;
 58     RETURN;
 59  END test_comp_report_func;
 60  /

Warning: Function created with compilation errors.
LINE/COL ERROR
-------- -----------------------------------------------------------------
51/7     PL/SQL: SQL Statement ignored
52/15    PLS-00386: type mismatch found at 'RES_REC' between FETCH cursor
         and INTO variables
  

Здесь проблема возникает из-за того, что в вашем операторе select количество столбцов не совпадает с количеством полей в вашей записи. Вы можете использовать %rowcount для предотвращения этого:

 CREATE OR REPLACE FUNCTION test_comp_report_func
   RETURN test_comp_report_tab_type
   PIPELINED IS
   v_row   test_COMP_REPORT_ROW_TYPE;
   CURSOR rrc_rectyp IS
      SELECT PON, RPON, SUPPLIER_NAME VENDOR, SUB_SUPPLIER_NAME SUB_SUPPLIER,
             SOURCE_NO, CKR, LEC_ID, ICSC, ACTL_ST, ADW_ST STATE, PROJ_ID,
             ACTION, SERVICE SPEED, AFP, ACNA, SERVICE_NAME, IE_DT,
             MOVE_TO_INV_DT EVENTDAT, DDD_DT DUEDATE, EFF_BILL_DT, ACOST
        FROM UNIVERSE;
   res_rec rrc_rectyp%ROWTYPE;
BEGIN
   OPEN rrc_rectyp;
   LOOP
      FETCH rrc_rectyp
         INTO res_rec;
      EXIT WHEN rrc_rectyp%NOTFOUND;
      v_row.pon := res_rec.pon;
      /*...*/
      PIPE ROW(v_row);
   END LOOP;
   RETURN;
END test_comp_report_func;
  

Вы даже можете извлечь объект SQL напрямую (с помощью неявного курсора):

 CREATE OR REPLACE FUNCTION test_comp_report_func
   RETURN test_comp_report_tab_type
   PIPELINED IS
BEGIN
   FOR res_rec IN (SELECT test_comp_report_row_type(PON, RPON, SUPPLIER_NAME,
                                                     SUB_SUPPLIER_NAME,SOURCE_NO,
                                                     CKR, LEC_ID, ICSC, ACTL_ST,
                                                     PROJ_ID, ACTION, SERVICE,
                                                     SERVICE_NAME, IE_DT, DDD_DT,
                                                     EFF_BILL_DT, ACOST)my_object
                     FROM UNIVERSE) LOOP
      PIPE ROW(res_rec.my_object);
   END LOOP;
   RETURN;
END test_comp_report_func;
  

Комментарии:

1. Я делал нечто подобное. Можете ли вы предложить несколько указаний, на которые мне следует обратить особое внимание при попытке найти проблему здесь?

2. @Rachel: можете ли вы предоставить полный тестовый пример, в котором это действительно приводит к ORA-00932?

3. select * from table(test_comp_report_func('01/01/2000','12/31/2009', null)) если вы видите мою функцию, она принимает начальную дату, конечную дату и год, и в запросе я нигде не использую значение year. Также, когда я запускаю запрос непосредственно из редактора разработчика sql, я могу получить данные, но когда я вызываю функцию с помощью инструкции select выше, это выдает мне указанную ошибку.

4. Позвольте мне обновить вопрос фактическими значениями, которые я использую, чтобы вы могли воспроизвести ошибку на своей стороне.

5. Я обновил вопрос и теперь получаю ошибку ORA-00932 в LOOP после открытия rec_cursor, не уверен, почему? Есть предложения.

Ответ №2:

Вы получаете ошибку, потому что SQL-запрос в e_sql возвращает на четыре значения больше, чем в res_rec . Курсор возвращает 21 столбец данных, но ваш recordvar тип записи содержит только 17 полей.

Мне кажется, что столбцы ACTL_ST , AFP , ACNA и EFF_BILL_DT ни с чем не сопоставляются в res_rec , и если вы удалите их из запроса, вы должны обнаружить, что ваша функция больше не сообщает об inconsistent datatypes ошибке.

Я бы, вероятно, реализовал функцию примерно следующим образом:

 CREATE OR REPLACE FUNCTION test_comp_report_func_2 (
  start_dt_h IN VARCHAR2 DEFAULT NULL,
  end_dt_h   IN VARCHAR2 DEFAULT NULL,
  year_h     IN VARCHAR2 DEFAULT NULL
) RETURN test_comp_report_tab_type PIPELINED
IS
  CURSOR cur_res_rec IS
    SELECT PON,
           RPON,
           SUPPLIER_NAME VENDOR, 
           SUB_SUPPLIER_NAME SUB_SUPPLIER, 
           SOURCE_NO,
           CKR ATT_CKT_ID,
           LEC_ID,
           ICSC, 
           ACTL_ST,
           ADW_ST STATE, 
           PROJ_ID AS PROJECT_ID,
           ACTION,
           SERVICE SERVICE_SPEED, 
           AFP,
           ACNA,
           SERVICE_NAME,
           IE_DT INEFFECT_DATE, 
           MOVE_TO_INV_DT EVENT_DATE,
           DDD_DT DUE_DATE, 
           EFF_BILL_DT,
           ACOST
      FROM UNIVERSE
     WHERE TO_DATE(IE_DT) BETWEEN TO_DATE(NVL('01/01/2000', '01/01/2000'), 'MM/DD/YYYY') 
                              AND TO_DATE(NVL('12/31/2009', TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, 12),'year') - 1,'MM/DD/YYYY')), 'MM/DD/YYYY')
       AND PROGRAM = 'T45sONNET'
       AND NVL(TRIM(ACOST_IND), 'NULL') NOT IN ('INVALID-2005')
     ORDER BY ACTION;

    v_row test_comp_report_row_type := test_comp_report_row_type(NULL, NULL, NULL, NULL,
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);

BEGIN  
  FOR res_rec IN cur_res_rec
  LOOP
      v_row.PON            := res_rec.PON;
      v_row.RPON           := res_rec.RPON;
      v_row.VENDOR         := res_rec.VENDOR;
      v_row.SUB_SUPPLIER   := res_rec.SUB_SUPPLIER;
      v_row.SOURCE_NO      := res_rec.SOURCE_NO;
      v_row.ATT_CKT_ID     := res_rec.ATT_CKT_ID;
      v_row.LEC_ID         := res_rec.LEC_ID;
      v_row.ICSC           := res_rec.ICSC;
      v_row.STATE          := res_rec.STATE;
      v_row.PROJECT_ID     := res_rec.PROJECT_ID;
      v_row.ACTION         := res_rec.ACTION;
      v_row.SERVICE_SPEED  := res_rec.SERVICE_SPEED;
      v_row.SERVICE_NAME   := res_rec.SERVICE_NAME;
      v_row.INEFFECT_DATE  := res_rec.INEFFECT_DATE;
      v_row.EVENT_DATE     := res_rec.EVENT_DATE;
      v_row.DUE_DATE       := res_rec.DUE_DATE;
      v_row.ACOST          := res_rec.ACOST;
      PIPE ROW(v_row);
  END LOOP;

  RETURN;

END test_comp_report_func_2;
/
  

Во-первых, я, честно говоря, не вижу причины, по которой вы используете динамический SQL. Приведенная выше функция использует «статический» SQL-запрос, и ее преимущество в том, что Oracle проверит, что этот запрос действителен, когда он компилирует функцию. Если в запросе ошибка, функция не будет компилироваться. С другой стороны, если у вас возникла ошибка с динамическим SQL-запросом, вы не узнаете о проблеме, пока не запустите свою функцию.

Динамический SQL полезен, если вы хотите изменить структуру запроса, например, выполнить его в разных таблицах или изменить столбцы, используемые в WHERE предложении. Однако в большинстве случаев вам не нужно этого делать. Динамический SQL — это одна из тех вещей, которые вам действительно не следует использовать, если вам не нужно его использовать.

Кроме того, с помощью FOR some_record IN some_cursor мне не нужно возиться с открытием и закрытием курсора, и мне не нужно проверять, остались ли еще какие-либо данные, и выходить из цикла, если да. Это также устраняет необходимость объявлять переменную для записи строки ( res_rec ) или неправильно указывать тип этой переменной. Для меня все это делается автоматически.

Комментарии:

1. Спасибо Luke за информацию. Как я упоминал в другом комментарии, я довольно новичок в PL / SQL и поэтому не знаю, каковы наилучшие методы и какой из них использовать в этом случае, но это, безусловно, помогло бы и помогло бы добавить некоторые знания.