动态 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_first
或 p_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;
我有一个 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_first
或 p_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;