必须用 DBMS_SQL.DESCRIBE_COLUMNS 获取列元信息,因 EXECUTE IMMEDIATE 要求预知列数与类型,无法处理动态列;需先 OPEN_CURSOR、PARSE,再 DESCRIBE_COLUMNS 获取 desc_tab,依 col_type 动态 DEFINE_COLUMN,最后安全 CLOSE_CURSOR。
直接用 DBMS_SQL 执行未知列的查询是可行的,但必须跳过「提前声明接收变量」这一步——因为列名、类型、数量全未知,硬编码 v_name、v_date 会立刻报错。
EXECUTE IMMEDIATE 要求你提前知道返回几列、每列什么类型,否则编译就失败。比如 EXECUTE IMMEDIATE 'SELECT * FROM t' INTO ...,省略 INTO 部分会报 ORA-00905;写了又得一一匹配,根本没法应对「列动态变化」的场景。
EXECUTE IMMEDIATE 对 SELECT 必须有 INTO 或 BULK COLLECT INTO,没得绕WHERE 1=0,DML/DDL 仍会真实执行(如删空表、建临时索引),风险不可控核心动作是调用 DBMS_SQL.DESCRIBE_COLUMNS,它把运行时解析出的列名、类型、长度等写进 dbms_sql.desc_tab 类型的集合里,后续才能按需定义接收变量。
PARSE,且不能跳过 OPEN_CURSOR ——游标未打开就调 DESCRIBE_COLUMNS 会报 ORA-01001col_type 字段值对应 Oracle 内部类型码:1=VARCHAR2,2=NUMBER,12=DATE,100=BOOLEAN(极少用),需逐个判断分支处理UPPER(name)),col_name 可能为空或为 "UPPER(NAME)",不能直接当字段名用DEFINE_COLUMN 的第三个参数(长度)对字符类型是强制的,但传小了会截断,传大了浪费内存;对 NUMBER/DATE 类型可省略,但显式写出更安全。
desctab(i).col_max_len,不是随便写 100 或 4000NUMBER(38) 接收,避免 ORA-06502 精度溢出DATE 类型变量,别试图用 VARCHAR2 中转格式化col_type = 112(CLOB)或 113(BLOB),得换用 DEFINE_COLUMN_LONG,否则 fetch 时崩很多脚本在异常路径里无条件调 DBMS_SQL.CLOSE_CURSOR,但若游标根本没成功打开(比如 PARSE 失败),关它就会触发 ORA-01001,掩盖原始错误。
DBMS_SQL.IS_OPEN(curid) 判断后再关,别信「肯定打开了」CLOSE_CURSOR 保底——万一 OPEN_CURSOR 都失败了,curid 是 NULL,关它照样报错OPEN_CURSOR 不报错,但后续 PARSE 会覆盖旧状态,旧结果集丢失且不可回溯