简体   繁体   English

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

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

I have the following DB2 stored procedure that allows the user to specify a variable number of parameters (specifying NULL for parameters they don't want to provide), where each parameter can be used to filter records in a WHERE clause.我有以下 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
;

It's my understanding that this procedure will perform very poorly because the procedure's query plan will only be evaluated when the procedure is bound (or ran the first time?), where if different combinations of parameters are specified in later executions the original query plan will be used, resulting in the wrong plan/indexes being used (thus making the proc very slow).据我了解,此过程将执行得很差,因为该过程的查询计划只会在该过程被绑定(或第一次运行?)时进行评估,如果在以后的执行中指定了不同的参数组合,原始查询计划将是使用,导致使用错误的计划/索引(从而使 proc 非常慢)。

I read that SQL Server allows you to specify OPTION(RECOMPILE) in the procedure's definition to tell the server to re-evaluate query plan(s) for each execution of the procedure, however since I'm using DB2 I'm looking for a DB2 equivalent.我读到 SQL 服务器允许您在过程的定义中指定OPTION(RECOMPILE)以告诉服务器为每次执行过程重新评估查询计划,但是由于我使用的是 DB2 我正在寻找DB2 等效。

Is there a way in DB2 that you can tell a stored procedure to re-evaluate it's query plan each execution? DB2 中是否有一种方法可以告诉存储过程在每次执行时重新评估其查询计划? Do I have to use something like SET CURRENT QUERY OPTIMIZATION inside my procedure before any statements are ran?在运行任何语句之前,我是否必须在我的过程中使用SET CURRENT QUERY OPTIMIZATION之类的东西?

Using dynamic SQL in your routine (instead of static SQL) is one way to ensure the query gets compiled each time (while it is different from a previous instantiation, or not in the package-cache etc).在您的例程中使用动态 SQL(而不是 static SQL)是确保每次编译查询的一种方法(虽然它与以前的实例不同,或者不在包缓存等中)。

In this case in your routine, you can use EXECUTE IMMEDIATE on a query you dynamically create inside the routine, or use PREPARE and EXECUTE.在这种情况下,在您的例程中,您可以在例程中动态创建的查询上使用 EXECUTE IMMEDIATE,或者使用 PREPARE 和 EXECUTE。 In both cases the full query does not exist until run time immediately before execution.在这两种情况下,完整的查询直到执行前的运行时才存在。 So the SQL string that you build can be different each time, including (for execute) a different number of parameter-markers ( ? ) each time and a matching number of bind parameters.因此,您构建的 SQL 字符串每次都可能不同,包括(用于执行)每次不同数量的参数标记( ? )和匹配数量的绑定参数。 The SQL query itself is not static - it can be different every time the procedure runs - varying with the (sanitized) parameter inputs to the stored procedure. SQL 查询本身不是 static - 每次过程运行时它都可能不同 - 随存储过程的(已清理)参数输入而变化。

For static-SQL, take a look at stored procedure SET_ROUTINE_OPTS , with the REOPT ALWAYS which needs to be done at build time, or you can use the registry variable DB2_SQLROUTINE_PREPOPTS to supply this instruction globally (usually this is unwise).对于静态 SQL,请查看存储过程SET_ROUTINE_OPTS ,其中REOPT ALWAYS需要在构建时完成,或者您可以使用注册表变量DB2_SQLROUTINE_PREPOPTS全局提供此指令(通常这是不明智的)。 You (or the DBA) can also arrange to rebind the static package corresponding to the sproc, with REOPT ALWAYS.您(或 DBA)也可以安排重新绑定与 sproc 对应的 static package,始终使用 REOPT。

You may try the DMBS_SQL module.你可以试试DMBS_SQL模块。
It looks a little bit lengthy for such a simple purpose, but it does the work.对于这样一个简单的目的,它看起来有点冗长,但它确实有效。
The final SELECT statement is constructed depending on each parameter nullability.最终的 SELECT 语句是根据每个参数的可空性构造的。
XML local variables are used to accumulate the rows fetched to return them from SP finally. XML 局部变量用于累积获取的行以最终从 SP 返回。 You may probably use some [C|G]GTT or ARRAY variable instead of these XML variables for that.您可能会使用一些 [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@  

Usage:用法:

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