繁体   English   中英

如何让 DB2 每次执行都重新评估查询计划?

[英]How to make DB2 re-evaluate the query plan each execution?

我有以下 DB2 存储过程,它允许用户指定可变数量的参数(为他们不想提供的参数指定 NULL),其中每个参数都可用于过滤 WHERE 子句中的记录。

CREATE PROCEDURE XX.DUMMY(IN I_FIRST_NAME VARCHAR(32),
                            I_LAST_NAME VARCHAR(100),
                            I_COMPANY_NAME VARCHAR(100))
SPECIFIC XX.DUMMY
RESULT SETS 1
MODIFIES SQL DATA
NOT DETERMINISTIC
NULL CALL
LANGUAGE SQL EXTERNAL ACTION
INHERIT SPECIAL REGISTERS
BEGIN
    DECLARE RESULTS CURSOR WITH RETURN TO CLIENT FOR
        WITH RELEVANT_RECORDS (RECORD_ID) AS (
            SELECT RECORD_ID
            FROM XX.RECORDS R
            WHERE (I_FIRST_NAME IS NULL OR R.SEARCH_FIRST_NAME = I_FIRST_NAME)
            AND (I_LAST_NAME IS NULL OR R.SEARCH_LAST_NAME = I_LAST_NAME)
            AND (I_COMPANY_NAME IS NULL OR R.SEARCH_COMPANY_NAME = I_COMPANY_NAME)
            WITH UR
        )
        SELECT RR.RECORD_ID, RD.*
        FROM RELEVANT_RECORDS RR
        JOIN XX.RECORD_DETAILS RD ON RD.RECORD_ID = RR.RECORD_ID
        FOR READ ONLY WITH UR;
    OPEN RESULTS;
END
;

据我了解,此过程将执行得很差,因为该过程的查询计划只会在该过程被绑定(或第一次运行?)时进行评估,如果在以后的执行中指定了不同的参数组合,原始查询计划将是使用,导致使用错误的计划/索引(从而使 proc 非常慢)。

我读到 SQL 服务器允许您在过程的定义中指定OPTION(RECOMPILE)以告诉服务器为每次执行过程重新评估查询计划,但是由于我使用的是 DB2 我正在寻找DB2 等效。

DB2 中是否有一种方法可以告诉存储过程在每次执行时重新评估其查询计划? 在运行任何语句之前,我是否必须在我的过程中使用SET CURRENT QUERY OPTIMIZATION之类的东西?

在您的例程中使用动态 SQL(而不是 static SQL)是确保每次编译查询的一种方法(虽然它与以前的实例不同,或者不在包缓存等中)。

在这种情况下,在您的例程中,您可以在例程中动态创建的查询上使用 EXECUTE IMMEDIATE,或者使用 PREPARE 和 EXECUTE。 在这两种情况下,完整的查询直到执行前的运行时才存在。 因此,您构建的 SQL 字符串每次都可能不同,包括(用于执行)每次不同数量的参数标记( ? )和匹配数量的绑定参数。 SQL 查询本身不是 static - 每次过程运行时它都可能不同 - 随存储过程的(已清理)参数输入而变化。

对于静态 SQL,请查看存储过程SET_ROUTINE_OPTS ,其中REOPT ALWAYS需要在构建时完成,或者您可以使用注册表变量DB2_SQLROUTINE_PREPOPTS全局提供此指令(通常这是不明智的)。 您(或 DBA)也可以安排重新绑定与 sproc 对应的 static package,始终使用 REOPT。

你可以试试DMBS_SQL模块。
对于这样一个简单的目的,它看起来有点冗长,但它确实有效。
最终的 SELECT 语句是根据每个参数的可空性构造的。
XML 局部变量用于累积获取的行以最终从 SP 返回。 您可能会使用一些 [C|G]GTT 或 ARRAY 变量来代替这些 XML 变量。

--#SET TERMINATOR @

-- Dynamic number of parameters demo
CREATE OR REPLACE PROCEDURE TEST_DBMS_SQL
(
  P_TABSCHEMA VARCHAR(128)
, P_TABNAME   VARCHAR(128)
)
DYNAMIC RESULT SETS 1
BEGIN
  DECLARE curid       INTEGER;
  DECLARE v_sql       VARCHAR(1000);
  DECLARE v_tabschema VARCHAR(128);
  DECLARE v_tabname   VARCHAR(128);
  DECLARE v_status    INTEGER;
  DECLARE v_doc       XML;
  DECLARE v_node      XML;
  
  DECLARE C1 CURSOR WITH RETURN FOR
  SELECT TABSCHEMA, TABNAME
  FROM XMLTABLE 
  (
    '$D/NODE' PASSING v_doc AS "D"
    COLUMNS
      TABSCHEMA VARCHAR(128) PATH 'TABSCHEMA'
    , TABNAME   VARCHAR(128) PATH 'TABNAME'
  );
  
  SET v_doc = XMLELEMENT(NAME "DOC");
  SET v_sql = 'SELECT TABSCHEMA, TABNAME FROM SYSCAT.TABLES WHERE 1=1';
  IF P_TABSCHEMA IS NOT NULL THEN
    SET v_sql = v_sql || ' AND TABSCHEMA = :p_tabschema'; 
  END IF;
  IF P_TABNAME IS NOT NULL THEN 
    SET v_sql = v_sql || ' AND TABNAME   = :p_tabname'; 
  END IF;

  CALL DBMS_SQL.OPEN_CURSOR(curid);
  CALL DBMS_SQL.PARSE(curid, v_sql, DBMS_SQL.native);
  
  IF P_TABSCHEMA IS NOT NULL THEN 
    CALL DBMS_SQL.BIND_VARIABLE_VARCHAR(curid, ':p_tabschema', P_TABSCHEMA);
  END IF;
  IF P_TABNAME IS NOT NULL THEN 
    CALL DBMS_SQL.BIND_VARIABLE_VARCHAR(curid, ':p_tabname', P_TABNAME);
  END IF;

  CALL DBMS_SQL.DEFINE_COLUMN_VARCHAR(curid, 1, v_tabschema, 128);
  CALL DBMS_SQL.DEFINE_COLUMN_VARCHAR(curid, 2, v_tabname, 128);

  CALL DBMS_SQL.EXECUTE(curid, v_status);
  --CALL DBMS_OUTPUT.PUT_LINE('Execute: ' || v_status);
  
  FETCH_LOOP: LOOP
  
    CALL DBMS_SQL.FETCH_ROWS(curid, v_status);
    IF v_status = 0 THEN LEAVE FETCH_LOOP; END IF;
    --CALL DBMS_OUTPUT.PUT_LINE('Fetch: ' || v_status);
    CALL DBMS_SQL.COLUMN_VALUE_VARCHAR(curid, 1, v_tabschema);
    CALL DBMS_SQL.COLUMN_VALUE_VARCHAR(curid, 2, v_tabname);
    --CALL DBMS_OUTPUT.PUT_LINE('TABSHEMA: ' || coalesce(v_tabschema, '*'));
    --CALL DBMS_OUTPUT.PUT_LINE('TABNAME: ' || coalesce(v_tabname, '*'));
    SET v_node = XMLELEMENT
    (
        NAME "NODE"
      , XMLELEMENT(NAME "TABSCHEMA", v_tabschema)
      , XMLELEMENT(NAME "TABNAME", v_tabname)
    );
  
    SET v_doc = XMLQUERY
    (
      'transform copy $mydoc := $doc modify do insert $node as last into $mydoc return $mydoc'
      passing v_doc as "doc", v_node as "node"
    );
  
  END LOOP FETCH_LOOP;
  
  --CALL DBMS_OUTPUT.PUT_LINE(XMLSERIALIZE(v_doc AS CLOB(10K)));
  CALL DBMS_SQL.CLOSE_CURSOR(curid);
  
  OPEN C1;
END@  

用法:

call test_dbms_sql(NULL, 'TABLES')@
call test_dbms_sql('SYSCAT', 'TABLES')@
call test_dbms_sql('SYSCAT', NULL)@
call test_dbms_sql(NULL, NULL)@

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM