Oracle sql - SP2-0552:绑定变量 "T_END" 未声明错误,但 "T_START" 没问题,为什么?两者都使用 TO_DATE()
Oracle sql - SP2-0552: Bind variable "T_END" not declared error, but "T_START" is fine, why? Both use TO_DATE()
检查这个 Oracle sql 脚本:
SET SERVEROUTPUT ON SIZE 200000;
declare
start timestamp;
fin timestamp;
opr varchar2(64);
op varchar2(64);
ext_act varchar2(64);
time_sum float;
counter integer := 1;
query1 varchar2(4096):='select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end and rownum < 500000 order by IH.EVENT_DATE';
query2 varchar2(4096):='select * from (select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end) where rownum < 500000';
query3 varchar2(4096):='select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end fetch first 500000 rows only';
begin
FOR query IN (SELECT column_value FROM table(sys.dbms_debug_vc2coll(query1, query2, query3))) loop
dbms_output.put_line('This is query ' || counter);
for i in 1..10 loop
start:=systimestamp;
exec :opr := '88000001';
exec :op := 'CHANGE_OWNER';
exec :ext_act := 'NOT_APPLICABLE';
exec :t_start :=TO_DATE('2018/07/01', 'yyyy/mm/dd');
exec :t_end :=TO_DATE('2020/05/01', 'yyyy/mm/dd');
query;
fin := systimestamp;
duration := fin - start;
dbms_output.put_line('Time taken for query' || counter || ' for run ' || i || ': ' || duration);
time_sum := time_sum || duration;
end loop;
dbms_output.put_line('Avg time for query' || counter || ': ' || time_sum / 10);
time_sum := 0;
counter:=counter+1;
end loop;
end
我有错误:SP2-0552: Bind variable "T_END" not declared.
,但是 T_START
没问题,并且在 T_END
之前。如果 T_END
不好,为什么 T_START
不好?怎么了?
本来是t1
和t2
,但是错误是一样的:t1
没有错误,t2
有错误。
If T_END
is bad, why T_START
is not?
它们都不好 - 都没有声明。
SQL 开发人员(我猜 - 或 SQL*Plus,或 SQLcl)正在报告它发现的第一个高级问题,这是一个未声明的绑定变量.在那个级别,它不会查看 PL/SQL 本身或将任何内容传递给数据库引擎进行验证,它会进行自己的扫描以查看它是否认为代码是合理的 - 并决定它不是,所以它立即停止,没有做任何实际工作。
它正在向后解析块,因此它首先看到并报告 t_end
,然后停在那里。如果您注释掉该行 (23),那么它将报告 t_start
is not declared instead.
None 您引用的绑定变量是在客户端中定义的 - 您需要 variable
和 exec
命令 before PL/SQL块; exec
本身是匿名 PL/SQL 块的客户端 shorthand,因此它不属于您拥有它的位置 - 它是客户端命令,而不是 PL/SQL 或 SQL.
您的代码中的其他一些问题:
- 您正在使用非法的保留字(如
start
);
- 您已将
opr
、op
和 ext_act
声明为局部 PL/SQL 变量 - 未 绑定变量 - 但是不要使用那些;
- 您已将
time_sum
声明为浮点数,但您正在处理时间戳之间的差异,即时间间隔;
- 你指的是
query
就好像它是一个值,但它实际上是一个记录 - 要使用由游标查询编辑的值 return 你需要引用 query.column_value
,或在查询中使用别名以获取更有用的名称;
- 你试图 运行
query;
就好像它是一个完整的语句而不是变量(或记录,见上一点) - 你需要将其视为动态 SQL,这导致:
- 您需要使用
query.column_value
作为动态游标并遍历结果,或者将查询更改为 return 单个标量值,您仍然需要 select 进入某事;
time_sum || duration
正在进行字符串连接 - 您可能打算使用加法;
- 可能还有其他一些我忘记的事情。
我认为这符合您的目标:
declare
l_start timestamp;
l_fin timestamp;
l_result pls_integer;
l_duration interval day to second;
l_time_sum interval day to second := interval '0' second;
l_counter integer := 1;
l_query1 varchar2(4096):='select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end and rownum < 500000 order by IH.EVENT_DATE';
l_query2 varchar2(4096):='select count(*) from (select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end) where rownum < 500000';
l_query3 varchar2(4096):='select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end fetch first 500000 rows only';
begin
FOR query IN (SELECT column_value as str FROM table(sys.dbms_debug_vc2coll(l_query1, l_query2, l_query3))) loop
dbms_output.put_line('This is query ' || l_counter);
for i in 1..10 loop
l_start:=systimestamp;
execute immediate query.str
into l_result -- not used
using '88000001', -- opr
'CHANGE_OWNER', -- op
'NOT_APPLICABLE', -- ext_act
DATE '2018-07-01', -- t_start
DATE '2020-05-01'; -- t_end
l_fin := systimestamp;
l_duration := l_fin - l_start;
dbms_output.put_line('Time taken for query' || l_counter || ' for run ' || i || ': ' || l_duration);
l_time_sum := l_time_sum + l_duration;
end loop;
dbms_output.put_line('Avg time for query' || l_counter || ': ' || (l_time_sum / 10));
l_time_sum := interval '0' second;
l_counter:=l_counter+1;
end loop;
end;
/
您也可以将查询字符串放入一个集合中,然后循环遍历它,而不必使用游标和查询:
declare
l_start timestamp;
l_fin timestamp;
l_result pls_integer;
l_duration interval day to second;
l_time_sum interval day to second := interval '0' second;
l_queries sys.odcivarchar2list := new sys.odcivarchar2list(
'select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end and rownum < 500000 order by IH.EVENT_DATE',
'select count(*) from (select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end) where rownum < 500000',
'select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end fetch first 500000 rows only');
begin
for l_counter in 1..l_queries.count loop
dbms_output.put_line('This is query ' || l_counter);
for i in 1..10 loop
l_start:=systimestamp;
execute immediate l_queries(l_counter)
into l_result -- not used
using '88000001', -- opr
'CHANGE_OWNER', -- op
'NOT_APPLICABLE', -- ext_act
DATE '2018-07-01', -- t_start
DATE '2020-05-01'; -- t_end
l_fin := systimestamp;
l_duration := l_fin - l_start;
dbms_output.put_line('Time taken for query' || l_counter || ' for run ' || i || ': ' || l_duration);
l_time_sum := l_time_sum + l_duration;
end loop;
dbms_output.put_line('Avg time for query' || l_counter || ': ' || (l_time_sum / 10));
l_time_sum := interval '0' second;
end loop;
end;
/
db<>fiddle 使用虚拟动态查询,因为我们没有您的 table(但希望您也没有名为 package
的 table?)
如果您不想直接在 using
子句中传递文字,那么您可以声明并填充局部 PL/SQL 变量并引用它们;或者您可以声明客户端级绑定变量 (var
/exec
),但这样做似乎没有任何优势。
我已经 guessed/assumed 一个计数会给你你想要的时间信息,允许一个单一的标量结果,你无论如何都会忽略它。这比遍历实际结果更简单——而且您似乎不太可能尝试比较 50000 行的总体提取时间。此外,第一个动态查询中的 order by
可能没有按照您的想法进行,这意味着这三个并不完全相同。
检查这个 Oracle sql 脚本:
SET SERVEROUTPUT ON SIZE 200000;
declare
start timestamp;
fin timestamp;
opr varchar2(64);
op varchar2(64);
ext_act varchar2(64);
time_sum float;
counter integer := 1;
query1 varchar2(4096):='select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end and rownum < 500000 order by IH.EVENT_DATE';
query2 varchar2(4096):='select * from (select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end) where rownum < 500000';
query3 varchar2(4096):='select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end fetch first 500000 rows only';
begin
FOR query IN (SELECT column_value FROM table(sys.dbms_debug_vc2coll(query1, query2, query3))) loop
dbms_output.put_line('This is query ' || counter);
for i in 1..10 loop
start:=systimestamp;
exec :opr := '88000001';
exec :op := 'CHANGE_OWNER';
exec :ext_act := 'NOT_APPLICABLE';
exec :t_start :=TO_DATE('2018/07/01', 'yyyy/mm/dd');
exec :t_end :=TO_DATE('2020/05/01', 'yyyy/mm/dd');
query;
fin := systimestamp;
duration := fin - start;
dbms_output.put_line('Time taken for query' || counter || ' for run ' || i || ': ' || duration);
time_sum := time_sum || duration;
end loop;
dbms_output.put_line('Avg time for query' || counter || ': ' || time_sum / 10);
time_sum := 0;
counter:=counter+1;
end loop;
end
我有错误:SP2-0552: Bind variable "T_END" not declared.
,但是 T_START
没问题,并且在 T_END
之前。如果 T_END
不好,为什么 T_START
不好?怎么了?
本来是t1
和t2
,但是错误是一样的:t1
没有错误,t2
有错误。
If
T_END
is bad, whyT_START
is not?
它们都不好 - 都没有声明。
SQL 开发人员(我猜 - 或 SQL*Plus,或 SQLcl)正在报告它发现的第一个高级问题,这是一个未声明的绑定变量.在那个级别,它不会查看 PL/SQL 本身或将任何内容传递给数据库引擎进行验证,它会进行自己的扫描以查看它是否认为代码是合理的 - 并决定它不是,所以它立即停止,没有做任何实际工作。
它正在向后解析块,因此它首先看到并报告 t_end
,然后停在那里。如果您注释掉该行 (23),那么它将报告 t_start
is not declared instead.
None 您引用的绑定变量是在客户端中定义的 - 您需要 variable
和 exec
命令 before PL/SQL块; exec
本身是匿名 PL/SQL 块的客户端 shorthand,因此它不属于您拥有它的位置 - 它是客户端命令,而不是 PL/SQL 或 SQL.
您的代码中的其他一些问题:
- 您正在使用非法的保留字(如
start
); - 您已将
opr
、op
和ext_act
声明为局部 PL/SQL 变量 - 未 绑定变量 - 但是不要使用那些; - 您已将
time_sum
声明为浮点数,但您正在处理时间戳之间的差异,即时间间隔; - 你指的是
query
就好像它是一个值,但它实际上是一个记录 - 要使用由游标查询编辑的值 return 你需要引用query.column_value
,或在查询中使用别名以获取更有用的名称; - 你试图 运行
query;
就好像它是一个完整的语句而不是变量(或记录,见上一点) - 你需要将其视为动态 SQL,这导致: - 您需要使用
query.column_value
作为动态游标并遍历结果,或者将查询更改为 return 单个标量值,您仍然需要 select 进入某事; time_sum || duration
正在进行字符串连接 - 您可能打算使用加法;- 可能还有其他一些我忘记的事情。
我认为这符合您的目标:
declare
l_start timestamp;
l_fin timestamp;
l_result pls_integer;
l_duration interval day to second;
l_time_sum interval day to second := interval '0' second;
l_counter integer := 1;
l_query1 varchar2(4096):='select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end and rownum < 500000 order by IH.EVENT_DATE';
l_query2 varchar2(4096):='select count(*) from (select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end) where rownum < 500000';
l_query3 varchar2(4096):='select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end fetch first 500000 rows only';
begin
FOR query IN (SELECT column_value as str FROM table(sys.dbms_debug_vc2coll(l_query1, l_query2, l_query3))) loop
dbms_output.put_line('This is query ' || l_counter);
for i in 1..10 loop
l_start:=systimestamp;
execute immediate query.str
into l_result -- not used
using '88000001', -- opr
'CHANGE_OWNER', -- op
'NOT_APPLICABLE', -- ext_act
DATE '2018-07-01', -- t_start
DATE '2020-05-01'; -- t_end
l_fin := systimestamp;
l_duration := l_fin - l_start;
dbms_output.put_line('Time taken for query' || l_counter || ' for run ' || i || ': ' || l_duration);
l_time_sum := l_time_sum + l_duration;
end loop;
dbms_output.put_line('Avg time for query' || l_counter || ': ' || (l_time_sum / 10));
l_time_sum := interval '0' second;
l_counter:=l_counter+1;
end loop;
end;
/
您也可以将查询字符串放入一个集合中,然后循环遍历它,而不必使用游标和查询:
declare
l_start timestamp;
l_fin timestamp;
l_result pls_integer;
l_duration interval day to second;
l_time_sum interval day to second := interval '0' second;
l_queries sys.odcivarchar2list := new sys.odcivarchar2list(
'select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end and rownum < 500000 order by IH.EVENT_DATE',
'select count(*) from (select * from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end) where rownum < 500000',
'select count(*) from ITEM_HISTORY IH join PACKAGE P on P.PACKAGE_NAME = IH.PACKAGE_NAME where OPERATOR_ID = :opr and (IH.OPERATION != :op OR IH.EVENT_DATE = IH.INSTALLATION_DATE) and IH.EXTERNAL_SERVICE_ACTION != :ext_act and IH.EVENT_DATE >= :t_start and IH.EVENT_DATE < :t_end fetch first 500000 rows only');
begin
for l_counter in 1..l_queries.count loop
dbms_output.put_line('This is query ' || l_counter);
for i in 1..10 loop
l_start:=systimestamp;
execute immediate l_queries(l_counter)
into l_result -- not used
using '88000001', -- opr
'CHANGE_OWNER', -- op
'NOT_APPLICABLE', -- ext_act
DATE '2018-07-01', -- t_start
DATE '2020-05-01'; -- t_end
l_fin := systimestamp;
l_duration := l_fin - l_start;
dbms_output.put_line('Time taken for query' || l_counter || ' for run ' || i || ': ' || l_duration);
l_time_sum := l_time_sum + l_duration;
end loop;
dbms_output.put_line('Avg time for query' || l_counter || ': ' || (l_time_sum / 10));
l_time_sum := interval '0' second;
end loop;
end;
/
db<>fiddle 使用虚拟动态查询,因为我们没有您的 table(但希望您也没有名为 package
的 table?)
如果您不想直接在 using
子句中传递文字,那么您可以声明并填充局部 PL/SQL 变量并引用它们;或者您可以声明客户端级绑定变量 (var
/exec
),但这样做似乎没有任何优势。
我已经 guessed/assumed 一个计数会给你你想要的时间信息,允许一个单一的标量结果,你无论如何都会忽略它。这比遍历实际结果更简单——而且您似乎不太可能尝试比较 50000 行的总体提取时间。此外,第一个动态查询中的 order by
可能没有按照您的想法进行,这意味着这三个并不完全相同。