动态 sql 执行期间出现 PL/SQL 个错误

PL/SQL errors during dynamic sql execution

我有一个 PL/SQL 过程打印出正确的 sql 但是一旦我调用 EXECUTE,我得到一个错误 sql 没有正确结束。

这是我的程序

procedure my_proc(p_first VARCHAR2, p_second NUMBER, p_third NUMBER DEFAULT NULL, p_fouth NUMBER DEFAULT NULL)
IS
str varchar2(3200);
v1 VARCHAR2(20);
v2 VARCHAR2(20);
v3 VARCHAR2(20);
str2 varchar2(1000);

begin

str2:='SELECT t2.c1,t2.c2,t2.c3 FROM ';

if p_third=3 then
        str:= 't1,t2,t3 where t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=p_first ;' ;
elsif p_third=2 then
        str:=' t1,t2,t4 where t1.c1=t2.c1 and t2.c1=t4.c1 and t2.c1=p_second ;' ;
else
        str:='t2 where t2.c1=p_second ;';
end if;

str2:=str2 || str;

dbms_output.put_line(str2);
EXECUTE IMMEDIATE  str2 into v1,v2,v3;

--dbms_output.put_line(v1||','||v2||','||v3);

end;

当我注释掉 EXECUTE 语句时,我得到

SELECT t2.c1,t2.c2,t2.c3 FROM t1,t2,t4 where t1.c1=t2.c1 and t2.c1=t4.c1 and t2.c1=p_second;

看起来不错,所以我不确定为什么执行会给我 `SQL 命令未正确结束。

我尝试删除 sql 末尾的分号,但后来我得到 ORA-00904: "p_second": invalid identifier .

我做错了什么?

不能这样传递参数

't1,t2,t3 where t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=p_first ;'

改为

't1,t2,t3 where t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1='''||p_first ||''';'

您收到的错误是因为动态语句末尾不应有分号。那是一个客户端语句分隔符,不是语句本身的一部分,立即执行只接受单个语句。 (您得到的是 "ORA-00933: SQL command not properly ended" 还是 "ORA-00911: invalid character" 取决于您的 Oracle 版本)。

所以第一个修复是更改您的代码以删除那些:

if p_third=3 then
        str:= 't1,t2,t3 where t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=p_first' ;
elsif p_third=2 then
        str:=' t1,t2,t4 where t1.c1=t2.c1 and t2.c1=t4.c1 and t2.c1=p_second' ;
else
        str:='t2 where t2.c1=p_second';
end if;

但是当这些语句被执行时,它们处于一个单独的 SQL 上下文中,该上下文没有可见性或不知道 PL/SQL 变量和过程参数。它会尝试将 p_firstp_second 解释为列名,但会失败,而且它们不太可能存在(即使存在,也不会如您所愿)。

您可以按照@建议的那样直接连接参数(但仍然没有分号):

        str:= 't1,t2,t3 where t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=''' || p_first || '''';
        str:=' t1,t2,t4 where t1.c1=t2.c1 and t2.c1=t4.c1 and t2.c1=' || p_second;
        str:='t2 where t2.c1=' || p_second;

等,由于您需要正确处理日期等,因此与其他数据类型一起使用会稍微复杂一些。

但最好使用绑定变量;这意味着有单独的 execute immediate 调用,因此您可以为每个生成的查询提供相关的绑定值。类似于:

begin

  str := 'SELECT t2.c1,t2.c2,t2.c3 FROM ';

  if p_third=3 then
    str := str || 't1,t2,t3 where t1.c1=t2.c1 and t2.c1=t3.c1 and t1.c1=:p_first';
    dbms_output.put_line(str);
    execute immediate str into v1, v2, v3 using p_first;
  elsif p_third=2 then
    str := str || ' t1,t2,t4 where t1.c1=t2.c1 and t2.c1=t4.c1 and t2.c1=:p_second';
    dbms_output.put_line(str);
    execute immediate str into v1, v2, v3 using p_second;
  else
    str := str || 't2 where t2.c1=:p_second';
    dbms_output.put_line(str);
    execute immediate str into v1, v2, v3 using p_second;
  end if;

  --dbms_output.put_line(v1||','||v2||','||v3);

end;

除了 Alex 的回答,我还推荐 ANSI 连接语法:

begin

  str := 'SELECT t2.c1,t2.c2,t2.c3 FROM ';

  if p_third=3 then
    str := str || 't1 JOIN t2 ON t1.c1=t2.c1 JOIN t3 ON t2.c1=t3.c1 WHERE t1.c1=:p_first';
    dbms_output.put_line(str);
    execute immediate str into v1, v2, v3 using p_first;
  elsif p_third=2 then
    str := str || ' t1 JOIN t2 ON t1.c1=t2.c1 JOIN t4 ON t2.c1=t4.c1 where t2.c1=:p_second';
    dbms_output.put_line(str);
    execute immediate str into v1, v2, v3 using p_second;
  else
    str := str || 't2 where t2.c1=:p_second';
    dbms_output.put_line(str);
    execute immediate str into v1, v2, v3 using p_second;
  end if;

  --dbms_output.put_line(v1||','||v2||','||v3);

end;