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 不好?怎么了?

本来是t1t2,但是错误是一样的: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 您引用的绑定变量是在客户端中定义的 - 您需要 variableexec 命令 before PL/SQL块; exec 本身是匿名 PL/SQL 块的客户端 shorthand,因此它不属于您拥有它的位置 - 它是客户端命令,而不是 PL/SQL 或 SQL.

您的代码中的其他一些问题:

  • 您正在使用非法的保留字(如 start);
  • 您已将 opropext_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 可能没有按照您的想法进行,这意味着这三个并不完全相同。