【问题标题】:Errors in PLSQL -PLSQL 中的错误 -
【发布时间】:2018-06-19 10:14:39
【问题描述】:

早上,

我正在尝试编写一个脚本,将卸载表(UNLD 到 HDL 文件)转换为使用 PLSQL 创建一个平面文件。我在尝试运行它时不断遇到语法错误,希望能得到专家的帮助!

以下是错误:

错误(53,21):PLS-00330:类型名称或子类型名称的使用无效

错误(57,32):PLS-00222:此范围内不存在名为“UNLDTABLE”的函数

我们的猜测是 unldTable 变量被视为字符串,而不是数据库表对象(在 PLSQL 中没有真正的经验)

CREATE OR REPLACE PROCEDURE UNLD_TO_HDL (processComponent IN VARCHAR2)
IS
fHandle UTL_FILE.FILE_TYPE;
concatData VARCHAR2(240);
concatHDLMetaTags VARCHAR2(240);
outputFileName VARCHAR2(240);
TYPE rowArrayType IS TABLE OF VARCHAR2(240);
rowArray rowArrayType;
emptyArray rowArrayType;
valExtractArray rowArrayType;
hdlFileName VARCHAR2(240);
unldTable VARCHAR2(240);
countUNLDRows Number;
dataType VARCHAR2(240);
current_table VARCHAR2(30);
value_to_char VARCHAR2(240);

BEGIN 

SELECT HDL_FILE_NAME
INTO hdlFileName
FROM GNC_HDL_CREATION_PARAMS
WHERE PROCESS_COMPONENT = processComponent;

SELECT UNLD_TABLE
INTO unldTable
FROM GNC_HDL_CREATION_PARAMS
WHERE PROCESS_COMPONENT = processComponent
FETCH NEXT 1 ROWS ONLY;

SELECT LISTAGG(HDL_META_TAG,'|')
WITHIN GROUP(ORDER BY HDL_META_TAG)
INTO concatHDLMetaTags
FROM GNC_MIG_CONTROL
WHERE HDL_COMP = processComponent;
    
SELECT DB_FIELD
BULK COLLECT INTO valExtractArray
FROM GNC_MIG_CONTROL
WHERE HDL_COMP = processComponent
ORDER BY HDL_META_TAG;

fHandle := UTL_FILE.FOPEN('./', hdlFileName, 'W');
UTL_FILE.PUTF(fHandle, concatHDLMetaTags + '\n');

SELECT num_rows INTO countUNLDRows FROM user_tables where table_name = unldTable;
    
FOR row in 1..countUNLDRows LOOP
    rowArray := emptyArrayType;
    FOR value in 1..valExtractArray.COUNT LOOP
        rowArray.extend();
        SELECT data_type INTO dataType FROM all_tab_columns where table_name = unldTable AND column_name = valExtractArray(value);
        
        IF dataType = 'VARCHAR2' THEN (SELECT valExtractArray(value) INTO value_to_char FROM current_table WHERE ROWNUM = row); 
        ELSIF dataType = 'DATE' THEN (SELECT TO_CHAR(valExtractArray(value),'YYYY/MM/DD') INTO value_to_char FROM current_table WHERE ROWNUM = row);  
        ELSIF dataType = 'NUMBER' THEN (SELECT TO_CHAR(valExtractArray(value)) INTO value_to_char FROM current_table WHERE ROWNUM = row);
        ENDIF;
        
        rowArray(value) := value_to_char;
    END LOOP;
    concatData := NULL;
    FOR item in 1..rowArray.COUNT LOOP
        IF item = rowArray.COUNT
        THEN concatData := (COALESCE(concatData,'') || rowArray(item));
        ELSE concatData := (COALESCE(concatData,'') || rowArray(item) || '|');
        END IF;
    END LOOP;
    UTL_FILE.PUTF(fHandle, concatData + '/n');
END LOOP;
UTL_FILE.FCLOSE(fHandle);
END; 

谢谢,

亚当

【问题讨论】:

  • 我不确定这条线在做什么rowArray(value) := unldTable(row).valExtractArray(value);。 unldTable 不是 Varchar2(240) 吗?
  • 我同意 Chrisrs2292;编译器抱怨是因为它应该抱怨。您正在使用带有字符串的数组语法。此外,您不需要为每个不同的变量声明不同的数组类型。 COALESCE(concatdata,concatdata)背后的意图是什么?
  • 您好,感谢您的回答。 unldTable 包含一个 VARCHAR,它是我们感兴趣的表的名称。我已经更新了代码以尝试将其视为表而不是数组,但仍然没有运气。我在这里尝试做的是动态创建平面文件,我通过循环遍历表中的每一行并将该行中的每个值添加到数组来完成。在下一步中,我们将遍历并连接数组中的每个值并将其写入平面文件。
  • Coalesce 将 Null 替换为连接中的空字符串,抱歉更新了 PLSQL,因为它不正确。
  • 你是对的,unldTable 被定义为一个字符串,因为你声明它为unldTable VARCHAR2(240); 那是一个字符串。如果您希望它成为一个集合,只需使其成为您现有的集合类型之一(或者更好的是,定义一个字符串集合类型并将其重用于所有内容,而不是使用名称略有不同的三个相同类型)。

标签: sql plsql syntax


【解决方案1】:

我相信这只是您代码中的一个疏忽。您将 unldTable 定义为 varchar,它可以正确使用,直到您尝试访问它,就好像它是第 51 行上的 varray 一样

rowArray(value) := unldTable(row).valExtractArray(value);

鉴于您尚未将其定义为可变数组,unldTable(row) 正在让解释器相信您指的是一个函数。

编辑

现在您已继续前进,您应该解决在运行时未知的表上调用 SELECT 语句的问题。为此,您需要使用Dynamic SQL;您可以通过多种方式执行此操作,最直接的是您的情况下的 Execute immediate 语句:

mystatement := 'SELECT valExtractArray(value) INTO :value_to_char FROM ' || current_table || ' WHERE ROWNUM = ' || row; 
execute immediate mystatement USING OUT value_to_char;

【讨论】:

  • 是的,问题是unldTable变量是一个包含数据库表名的VARCHAR2,需要引用数据库表对象本身而不是VARCHAR。请参阅上面更新的代码...尝试引用为数据库表而不是数组,但仍然无法正常工作。
  • 您无法像尝试那样引用动态表名(“FROM unldTable WHERE”)。这不会编译,因为它会查找名为unldTable 的表。您必须使用动态 sql 来执行此操作。查看Performing SQL Operations with Native Dynamic SQL 中的构造。例如execute immediate my_string .
  • 非常感谢 Antonio,会调查此事
  • 查看了立即执行,但没有一个示例与我想做的类似。你能给我关于如何/在哪里使用立即执行的任何想法吗?泰
  • 我刚刚上传了我的答案,展示了如何实现这一目标
【解决方案2】:

看来你需要生成一个光标为

select [list of columns from GNC_MIG_CONTROL.DB_FIELD]
from   [table name from GNC_HDL_CREATION_PARAMS.UNLD_TABLE]

假设设置如下:

create table my_table (business_date date, id integer, dummy1 varchar2(1), dummy2 varchar2(20));
create table gnc_hdl_creation_params (unld_table varchar2(30), process_component varchar2(30));
create table gnc_mig_control (db_field varchar2(30), hdl_comp varchar2(30), hdl_meta_tag integer);

insert into my_table(business_date, id, dummy1, dummy2) values (date '2018-01-01', 123, 'X','Some more text');

insert into gnc_hdl_creation_params (unld_table, process_component) values ('MY_TABLE', 'XYZ');

insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('BUSINESS_DATE', 'XYZ', '1');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('ID', 'XYZ', '2');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('DUMMY1', 'XYZ', '3');
insert into gnc_mig_control (db_field, hdl_comp, hdl_meta_tag) values ('DUMMY2', 'XYZ', '4');

您可以像这样构建查询:

select unld_table, listagg(expr, q'[||'|'||]') within group (order by hdl_meta_tag) as expr_list
from   ( select t.unld_table
              , case tc.data_type
                    when 'DATE' then 'to_char('||c.db_field||',''YYYY-MM-DD'')'
                    else c.db_field
                end as expr
              , c.hdl_meta_tag
         from   gnc_hdl_creation_params t
                join gnc_mig_control c
                     on  c.hdl_comp = t.process_component
                left join user_tab_columns tc
                     on  tc.table_name = t.unld_table
                     and tc.column_name = c.db_field
         where  t.process_component = 'XYZ'
       )
group by unld_table;

输出:

UNLD_TABLE  EXPR_LIST
----------- --------------------------------------------------------------------------------
MY_TABLE    to_char(BUSINESS_DATE,'YYYY-MM-DD')||'|'||ID||'|'||DUMMY1||'|'||DUMMY2

现在,如果您将该逻辑插入到 PL/SQL 过程中,您可能会得到这样的结果:

declare
    processComponent constant gnc_hdl_creation_params.process_component%type := 'XYZ';
    unloadSQL long;
    unloadCur sys_refcursor;
    text long;
begin
    select 'select ' || listagg(expr, q'[||'|'||]') within group (order by hdl_meta_tag) || ' as text from ' || unld_table
    into unloadSQL
    from   ( select t.unld_table
                  , case tc.data_type
                        when 'DATE' then 'to_char('||c.db_field||',''YYYY/MM/DD'')'
                        else c.db_field
                    end as expr
                  , c.hdl_meta_tag
             from   gnc_hdl_creation_params t
                    join gnc_mig_control c
                         on  c.hdl_comp = t.process_component
                    left join user_tab_columns tc
                         on  tc.table_name = t.unld_table
                         and tc.column_name = c.db_field
             where  t.process_component = processComponent
           )
    group by unld_table;

    open unloadCur for unloadSQL;

    loop
        fetch unloadCur into text;
        dbms_output.put_line(text);
        exit when unloadCur%notfound;
    end loop;

    close unloadCur;
end;

输出:

2018/01/01|123|X|Some more text
2018/01/01|123|X|Some more text

现在你只需要把它变成一个程序,将dbms_output 更改为utl_file 并添加你的元标记等,你就在那里。

我假设每个流程组件只有一个不同的unld_table。如果还有更多,您将需要一个循环来处理每个。

对于更通用的方法,您可以构建一个可以封装数据类型处理的cursor-to-csv generator,然后您只需将SQL 构建为select [columns] from [table]。然后,您可以将通用游标写入文件处理器,在其中传入文件名和游标,它会做很多事情。

编辑:我已更新我的cursor-to-csv generator 以提供文件输出,因此您只需将光标和文件详细信息传递给它。

【讨论】:

    猜你喜欢
    • 2018-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 1970-01-01
    • 2023-02-26
    • 2014-11-29
    相关资源
    最近更新 更多